Merge "Fix ComponentResolver Computer consistency"
diff --git a/Android.bp b/Android.bp
index 1c4f10e..f805947 100644
--- a/Android.bp
+++ b/Android.bp
@@ -191,7 +191,6 @@
             "sax/java",
             "telecomm/java",
 
-            "apex/media/aidl/stable",
             // TODO(b/147699819): remove this
             "telephony/java",
         ],
@@ -289,6 +288,7 @@
             // TODO: remove when moved to the below package
             "frameworks/base/packages/ConnectivityT/framework-t/aidl-export",
             "packages/modules/Connectivity/framework/aidl-export",
+            "packages/modules/Media/apex/aidl/stable",
             "hardware/interfaces/graphics/common/aidl",
         ],
     },
@@ -538,6 +538,7 @@
             "frameworks/native/libs/permission/aidl",
             // TODO: remove when moved to the below package
             "frameworks/base/packages/ConnectivityT/framework-t/aidl-export",
+            "packages/modules/Media/apex/aidl/stable",
             "packages/modules/Connectivity/framework/aidl-export",
             "hardware/interfaces/graphics/common/aidl",
         ],
@@ -575,11 +576,9 @@
 stubs_defaults {
     name: "module-classpath-stubs-defaults",
     aidl: {
-        local_include_dirs: [
-            "apex/media/aidl/stable",
-        ],
         include_dirs: [
             "packages/modules/Connectivity/framework/aidl-export",
+            "packages/modules/Media/apex/aidl/stable",
         ],
     },
     libs: [
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 996cdc9..ba31161 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -145,11 +145,9 @@
         "api-versions-jars-dir",
     ],
     aidl: {
-        local_include_dirs: [
-            "apex/media/aidl/stable",
-        ],
         include_dirs: [
             "packages/modules/Connectivity/framework/aidl-export",
+            "packages/modules/Media/apex/aidl/stable",
         ],
     },
 }
diff --git a/apex/media/Android.bp b/apex/media/Android.bp
deleted file mode 100644
index 96e88dd..0000000
--- a/apex/media/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT 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 {
-    default_visibility: [
-        ":__subpackages__",
-        "//frameworks/av/apex",
-        "//frameworks/av/apex/testing",
-    ],
-    // See: http://go/android-license-faq
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-sdk {
-    name: "media-module-sdk",
-    bootclasspath_fragments: ["com.android.media-bootclasspath-fragment"],
-    systemserverclasspath_fragments: ["com.android.media-systemserverclasspath-fragment"],
-    java_sdk_libs: [
-        "framework-media",
-    ],
-}
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
deleted file mode 100644
index 2c5965c..0000000
--- a/apex/media/OWNERS
+++ /dev/null
@@ -1,12 +0,0 @@
-# Bug component: 1344
-hdmoon@google.com
-jinpark@google.com
-klhyun@google.com
-lnilsson@google.com
-sungsoo@google.com
-
-# go/android-fwk-media-solutions for info on areas of ownership.
-include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
-
-# media reliability team packages/delivers the media mainline builds.
-include platform/frameworks/av:/media/janitors/reliability_mainline_OWNERS
diff --git a/apex/media/aidl/Android.bp b/apex/media/aidl/Android.bp
deleted file mode 100644
index 4ba0d9b..0000000
--- a/apex/media/aidl/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// Copyright 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT 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 {
-    // See: http://go/android-license-faq
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
-    name: "stable-media-aidl-srcs",
-    srcs: ["stable/**/*.aidl"],
-    path: "stable",
-}
-
-filegroup {
-    name: "private-media-aidl-srcs",
-    srcs: ["private/**/I*.aidl"],
-    path: "private",
-}
-
-filegroup {
-    name: "media-aidl-srcs",
-    srcs: [
-        ":private-media-aidl-srcs",
-        ":stable-media-aidl-srcs",
-    ],
-}
diff --git a/apex/media/aidl/private/android/media/Controller2Link.aidl b/apex/media/aidl/private/android/media/Controller2Link.aidl
deleted file mode 100644
index 64edafc..0000000
--- a/apex/media/aidl/private/android/media/Controller2Link.aidl
+++ /dev/null
@@ -1,19 +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;
-
-parcelable Controller2Link;
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
deleted file mode 100644
index e1c89e9..0000000
--- a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media;
-
-import android.media.Session2Token;
-import android.media.IMediaCommunicationServiceCallback;
-import android.media.MediaParceledListSlice;
-import android.view.KeyEvent;
-
-/** {@hide} */
-interface IMediaCommunicationService {
-    void notifySession2Created(in Session2Token sessionToken);
-    boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
-    MediaParceledListSlice getSession2Tokens(int userId);
-
-   void dispatchMediaKeyEvent(String packageName, in KeyEvent keyEvent, boolean asSystemService);
-
-    void registerCallback(IMediaCommunicationServiceCallback callback, String packageName);
-    void unregisterCallback(IMediaCommunicationServiceCallback callback);
-}
-
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
deleted file mode 100644
index e347ebf..0000000
--- a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media;
-
-import android.media.Session2Token;
-import android.media.MediaParceledListSlice;
-
-/** {@hide} */
-oneway interface IMediaCommunicationServiceCallback {
-    void onSession2Created(in Session2Token token);
-    void onSession2Changed(in MediaParceledListSlice tokens);
-}
-
diff --git a/apex/media/aidl/private/android/media/IMediaController2.aidl b/apex/media/aidl/private/android/media/IMediaController2.aidl
deleted file mode 100644
index 42c6e70..0000000
--- a/apex/media/aidl/private/android/media/IMediaController2.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.media.Session2Command;
-
-/**
- * Interface from MediaSession2 to MediaController2.
- * <p>
- * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
- * and holds calls from session to make session owner(s) frozen.
- * @hide
- */
- // Code for AML only
-oneway interface IMediaController2 {
-    void notifyConnected(int seq, in Bundle connectionResult) = 0;
-    void notifyDisconnected(int seq) = 1;
-    void notifyPlaybackActiveChanged(int seq, boolean playbackActive) = 2;
-    void sendSessionCommand(int seq, in Session2Command command, in Bundle args,
-            in ResultReceiver resultReceiver) = 3;
-    void cancelSessionCommand(int seq) = 4;
-    // Next Id : 5
-}
diff --git a/apex/media/aidl/private/android/media/IMediaSession2.aidl b/apex/media/aidl/private/android/media/IMediaSession2.aidl
deleted file mode 100644
index 26e717b..0000000
--- a/apex/media/aidl/private/android/media/IMediaSession2.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.media.Controller2Link;
-import android.media.Session2Command;
-
-/**
- * Interface from MediaController2 to MediaSession2.
- * <p>
- * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
- * and holds calls from session to make session owner(s) frozen.
- * @hide
- */
- // Code for AML only
-oneway interface IMediaSession2 {
-    void connect(in Controller2Link caller, int seq, in Bundle connectionRequest) = 0;
-    void disconnect(in Controller2Link caller, int seq) = 1;
-    void sendSessionCommand(in Controller2Link caller, int seq, in Session2Command sessionCommand,
-            in Bundle args, in ResultReceiver resultReceiver) = 2;
-    void cancelSessionCommand(in Controller2Link caller, int seq) = 3;
-    // Next Id : 4
-}
diff --git a/apex/media/aidl/private/android/media/IMediaSession2Service.aidl b/apex/media/aidl/private/android/media/IMediaSession2Service.aidl
deleted file mode 100644
index 10ac1be..0000000
--- a/apex/media/aidl/private/android/media/IMediaSession2Service.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 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.media;
-
-import android.os.Bundle;
-import android.media.Controller2Link;
-
-/**
- * Interface from MediaController2 to MediaSession2Service.
- * <p>
- * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
- * and holds calls from controller to make controller owner(s) frozen.
- * @hide
- */
-oneway interface IMediaSession2Service {
-    void connect(in Controller2Link caller, int seq, in Bundle connectionRequest) = 0;
-    // Next Id : 1
-}
diff --git a/apex/media/aidl/private/android/media/Session2Command.aidl b/apex/media/aidl/private/android/media/Session2Command.aidl
deleted file mode 100644
index 43a7b12..0000000
--- a/apex/media/aidl/private/android/media/Session2Command.aidl
+++ /dev/null
@@ -1,19 +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;
-
-parcelable Session2Command;
diff --git a/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl b/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl
deleted file mode 100644
index 92d673f..0000000
--- a/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2020, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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;
-
-parcelable MediaParceledListSlice<T>;
diff --git a/apex/media/aidl/stable/android/media/Session2Token.aidl b/apex/media/aidl/stable/android/media/Session2Token.aidl
deleted file mode 100644
index c5980e9..0000000
--- a/apex/media/aidl/stable/android/media/Session2Token.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 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.media;
-
-parcelable Session2Token;
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
deleted file mode 100644
index e38488d..0000000
--- a/apex/media/framework/Android.bp
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT 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 {
-    // See: http://go/android-license-faq
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_library {
-    name: "updatable-media",
-
-    srcs: [
-        ":updatable-media-srcs",
-    ],
-
-    permitted_packages: [
-        "android.media",
-    ],
-
-    optimize: {
-        enabled: true,
-        shrink: true,
-        proguard_flags_files: ["updatable-media-proguard.flags"],
-    },
-
-    installable: true,
-
-    sdk_version: "module_current",
-    libs: [
-        "androidx.annotation_annotation",
-        "framework-annotations-lib",
-    ],
-    static_libs: [
-        "exoplayer2-extractor",
-        "mediatranscoding_aidl_interface-java",
-        "modules-annotation-minsdk",
-        "modules-utils-build",
-    ],
-    jarjar_rules: "jarjar_rules.txt",
-
-    plugins: ["java_api_finder"],
-
-    hostdex: true, // for hiddenapi check
-    apex_available: [
-        "com.android.media",
-        "test_com.android.media",
-    ],
-    min_sdk_version: "29",
-    lint: {
-        strict_updatability_linting: true,
-    },
-    visibility: [
-        "//frameworks/av/apex:__subpackages__",
-        "//frameworks/base/apex/media/service",
-        "//frameworks/base/api", // For framework-all
-        "//packages/modules/Media/apex/service",
-    ],
-}
-
-filegroup {
-    name: "updatable-media-srcs",
-    srcs: [
-        "java/android/media/MediaFrameworkInitializer.java",
-        ":media-aidl-srcs",
-        ":mediaparceledlistslice-java-srcs",
-        ":mediaparser-srcs",
-        ":mediasession2-java-srcs",
-        ":mediatranscoding-srcs",
-    ],
-    visibility: ["//frameworks/base"],
-}
-
-filegroup {
-    name: "mediasession2-java-srcs",
-    srcs: [
-        "java/android/media/Controller2Link.java",
-        "java/android/media/MediaConstants.java",
-        "java/android/media/MediaController2.java",
-        "java/android/media/MediaSession2.java",
-        "java/android/media/MediaSession2Service.java",
-        "java/android/media/Session2Command.java",
-        "java/android/media/Session2CommandGroup.java",
-        "java/android/media/Session2Link.java",
-        "java/android/media/Session2Token.java",
-        "java/android/media/MediaCommunicationManager.java",
-    ],
-    path: "java",
-}
-
-filegroup {
-    name: "mediaparceledlistslice-java-srcs",
-    srcs: [
-        "java/android/media/MediaParceledListSlice.java",
-        "java/android/media/BaseMediaParceledListSlice.java",
-    ],
-    path: "java",
-}
-
-filegroup {
-    name: "mediaparser-srcs",
-    srcs: [
-        "java/android/media/MediaParser.java",
-    ],
-    path: "java",
-}
-
-filegroup {
-    name: "mediatranscoding-srcs",
-    srcs: [
-        "java/android/media/ApplicationMediaCapabilities.java",
-        "java/android/media/MediaFeature.java",
-        "java/android/media/MediaTranscodingManager.java",
-    ],
-    path: "java",
-}
-
-java_sdk_library {
-    name: "framework-media",
-    defaults: ["framework-module-defaults"],
-
-    // This is only used to define the APIs for updatable-media.
-    api_only: true,
-
-    srcs: [
-        ":updatable-media-srcs",
-    ],
-
-    impl_library_visibility: ["//frameworks/av/apex:__subpackages__"],
-}
-
-cc_library_shared {
-    name: "libmediaparser-jni",
-    srcs: [
-        "jni/android_media_MediaParserJNI.cpp",
-    ],
-    header_libs: ["jni_headers"],
-    shared_libs: [
-        "libandroid",
-        "liblog",
-        "libmediametrics",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-unused-parameter",
-        "-Wunreachable-code",
-        "-Wunused",
-    ],
-    apex_available: [
-        "com.android.media",
-    ],
-    min_sdk_version: "29",
-}
diff --git a/apex/media/framework/TEST_MAPPING b/apex/media/framework/TEST_MAPPING
deleted file mode 100644
index 3d21914..0000000
--- a/apex/media/framework/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "CtsMediaParserTestCases"
-    },
-    {
-      "name": "CtsMediaParserHostTestCases"
-    }
-  ]
-}
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
deleted file mode 100644
index b7d7ed8..0000000
--- a/apex/media/framework/api/current.txt
+++ /dev/null
@@ -1,267 +0,0 @@
-// Signature format: 2.0
-package android.media {
-
-  public final class ApplicationMediaCapabilities implements android.os.Parcelable {
-    method @NonNull public static android.media.ApplicationMediaCapabilities createFromXml(@NonNull org.xmlpull.v1.XmlPullParser);
-    method public int describeContents();
-    method @NonNull public java.util.List<java.lang.String> getSupportedHdrTypes();
-    method @NonNull public java.util.List<java.lang.String> getSupportedVideoMimeTypes();
-    method @NonNull public java.util.List<java.lang.String> getUnsupportedHdrTypes();
-    method @NonNull public java.util.List<java.lang.String> getUnsupportedVideoMimeTypes();
-    method public boolean isFormatSpecified(@NonNull String);
-    method public boolean isHdrTypeSupported(@NonNull String);
-    method public boolean isVideoMimeTypeSupported(@NonNull String);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.ApplicationMediaCapabilities> CREATOR;
-  }
-
-  public static final class ApplicationMediaCapabilities.Builder {
-    ctor public ApplicationMediaCapabilities.Builder();
-    method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedHdrType(@NonNull String);
-    method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedVideoMimeType(@NonNull String);
-    method @NonNull public android.media.ApplicationMediaCapabilities.Builder addUnsupportedHdrType(@NonNull String);
-    method @NonNull public android.media.ApplicationMediaCapabilities.Builder addUnsupportedVideoMimeType(@NonNull String);
-    method @NonNull public android.media.ApplicationMediaCapabilities build();
-  }
-
-  public class MediaCommunicationManager {
-    method @NonNull public java.util.List<android.media.Session2Token> getSession2Tokens();
-    method @IntRange(from=1) public int getVersion();
-  }
-
-  public class MediaController2 implements java.lang.AutoCloseable {
-    method public void cancelSessionCommand(@NonNull Object);
-    method public void close();
-    method @Nullable public android.media.Session2Token getConnectedToken();
-    method public boolean isPlaybackActive();
-    method @NonNull public Object sendSessionCommand(@NonNull android.media.Session2Command, @Nullable android.os.Bundle);
-  }
-
-  public static final class MediaController2.Builder {
-    ctor public MediaController2.Builder(@NonNull android.content.Context, @NonNull android.media.Session2Token);
-    method @NonNull public android.media.MediaController2 build();
-    method @NonNull public android.media.MediaController2.Builder setConnectionHints(@NonNull android.os.Bundle);
-    method @NonNull public android.media.MediaController2.Builder setControllerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaController2.ControllerCallback);
-  }
-
-  public abstract static class MediaController2.ControllerCallback {
-    ctor public MediaController2.ControllerCallback();
-    method public void onCommandResult(@NonNull android.media.MediaController2, @NonNull Object, @NonNull android.media.Session2Command, @NonNull android.media.Session2Command.Result);
-    method public void onConnected(@NonNull android.media.MediaController2, @NonNull android.media.Session2CommandGroup);
-    method public void onDisconnected(@NonNull android.media.MediaController2);
-    method public void onPlaybackActiveChanged(@NonNull android.media.MediaController2, boolean);
-    method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaController2, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
-  }
-
-  public final class MediaFeature {
-    ctor public MediaFeature();
-  }
-
-  public static final class MediaFeature.HdrType {
-    field public static final String DOLBY_VISION = "android.media.feature.hdr.dolby_vision";
-    field public static final String HDR10 = "android.media.feature.hdr.hdr10";
-    field public static final String HDR10_PLUS = "android.media.feature.hdr.hdr10_plus";
-    field public static final String HLG = "android.media.feature.hdr.hlg";
-  }
-
-  public final class MediaParser {
-    method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException;
-    method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...);
-    method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer);
-    method @NonNull public android.media.metrics.LogSessionId getLogSessionId();
-    method @NonNull public String getParserName();
-    method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat);
-    method public void release();
-    method public void seek(@NonNull android.media.MediaParser.SeekPoint);
-    method public void setLogSessionId(@NonNull android.media.metrics.LogSessionId);
-    method @NonNull public android.media.MediaParser setParameter(@NonNull String, @NonNull Object);
-    method public boolean supportsParameter(@NonNull String);
-    field public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = "android.media.mediaparser.adts.enableCbrSeeking";
-    field public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "android.media.mediaparser.amr.enableCbrSeeking";
-    field public static final String PARAMETER_FLAC_DISABLE_ID3 = "android.media.mediaparser.flac.disableId3";
-    field public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = "android.media.mediaparser.matroska.disableCuesSeeking";
-    field public static final String PARAMETER_MP3_DISABLE_ID3 = "android.media.mediaparser.mp3.disableId3";
-    field public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "android.media.mediaparser.mp3.enableCbrSeeking";
-    field public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = "android.media.mediaparser.mp3.enableIndexSeeking";
-    field public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "android.media.mediaparser.mp4.ignoreEditLists";
-    field public static final String PARAMETER_MP4_IGNORE_TFDT_BOX = "android.media.mediaparser.mp4.ignoreTfdtBox";
-    field public static final String PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = "android.media.mediaparser.mp4.treatVideoFramesAsKeyframes";
-    field public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = "android.media.mediaparser.ts.allowNonIdrAvcKeyframes";
-    field public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = "android.media.mediaparser.ts.ignoreDetectAccessUnits";
-    field public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = "android.media.mediaparser.ts.enableHdmvDtsAudioStreams";
-    field public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "android.media.mediaparser.ts.ignoreAacStream";
-    field public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "android.media.mediaparser.ts.ignoreAvcStream";
-    field public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = "android.media.mediaparser.ts.ignoreSpliceInfoStream";
-    field public static final String PARAMETER_TS_MODE = "android.media.mediaparser.ts.mode";
-    field public static final String PARSER_NAME_AC3 = "android.media.mediaparser.Ac3Parser";
-    field public static final String PARSER_NAME_AC4 = "android.media.mediaparser.Ac4Parser";
-    field public static final String PARSER_NAME_ADTS = "android.media.mediaparser.AdtsParser";
-    field public static final String PARSER_NAME_AMR = "android.media.mediaparser.AmrParser";
-    field public static final String PARSER_NAME_FLAC = "android.media.mediaparser.FlacParser";
-    field public static final String PARSER_NAME_FLV = "android.media.mediaparser.FlvParser";
-    field public static final String PARSER_NAME_FMP4 = "android.media.mediaparser.FragmentedMp4Parser";
-    field public static final String PARSER_NAME_MATROSKA = "android.media.mediaparser.MatroskaParser";
-    field public static final String PARSER_NAME_MP3 = "android.media.mediaparser.Mp3Parser";
-    field public static final String PARSER_NAME_MP4 = "android.media.mediaparser.Mp4Parser";
-    field public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser";
-    field public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser";
-    field public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser";
-    field public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN";
-    field public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser";
-    field public static final int SAMPLE_FLAG_DECODE_ONLY = -2147483648; // 0x80000000
-    field public static final int SAMPLE_FLAG_ENCRYPTED = 1073741824; // 0x40000000
-    field public static final int SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA = 268435456; // 0x10000000
-    field public static final int SAMPLE_FLAG_KEY_FRAME = 1; // 0x1
-    field public static final int SAMPLE_FLAG_LAST_SAMPLE = 536870912; // 0x20000000
-  }
-
-  public static interface MediaParser.InputReader {
-    method public long getLength();
-    method public long getPosition();
-    method public int read(@NonNull byte[], int, int) throws java.io.IOException;
-  }
-
-  public static interface MediaParser.OutputConsumer {
-    method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
-    method public void onSampleDataFound(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException;
-    method public void onSeekMapFound(@NonNull android.media.MediaParser.SeekMap);
-    method public void onTrackCountFound(int);
-    method public void onTrackDataFound(int, @NonNull android.media.MediaParser.TrackData);
-  }
-
-  public static final class MediaParser.ParsingException extends java.io.IOException {
-  }
-
-  public static final class MediaParser.SeekMap {
-    method public long getDurationMicros();
-    method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
-    method public boolean isSeekable();
-    field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000
-  }
-
-  public static final class MediaParser.SeekPoint {
-    field @NonNull public static final android.media.MediaParser.SeekPoint START;
-    field public final long position;
-    field public final long timeMicros;
-  }
-
-  public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader {
-    method public void seekToPosition(long);
-  }
-
-  public static final class MediaParser.TrackData {
-    field @Nullable public final android.media.DrmInitData drmInitData;
-    field @NonNull public final android.media.MediaFormat mediaFormat;
-  }
-
-  public static final class MediaParser.UnrecognizedInputFormatException extends java.io.IOException {
-  }
-
-  public class MediaSession2 implements java.lang.AutoCloseable {
-    method public void broadcastSessionCommand(@NonNull android.media.Session2Command, @Nullable android.os.Bundle);
-    method public void cancelSessionCommand(@NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object);
-    method public void close();
-    method @NonNull public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
-    method @NonNull public String getId();
-    method @NonNull public android.media.Session2Token getToken();
-    method public boolean isPlaybackActive();
-    method @NonNull public Object sendSessionCommand(@NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
-    method public void setPlaybackActive(boolean);
-  }
-
-  public static final class MediaSession2.Builder {
-    ctor public MediaSession2.Builder(@NonNull android.content.Context);
-    method @NonNull public android.media.MediaSession2 build();
-    method @NonNull public android.media.MediaSession2.Builder setExtras(@NonNull android.os.Bundle);
-    method @NonNull public android.media.MediaSession2.Builder setId(@NonNull String);
-    method @NonNull public android.media.MediaSession2.Builder setSessionActivity(@Nullable android.app.PendingIntent);
-    method @NonNull public android.media.MediaSession2.Builder setSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaSession2.SessionCallback);
-  }
-
-  public static final class MediaSession2.ControllerInfo {
-    method @NonNull public android.os.Bundle getConnectionHints();
-    method @NonNull public String getPackageName();
-    method @NonNull public android.media.session.MediaSessionManager.RemoteUserInfo getRemoteUserInfo();
-    method public int getUid();
-  }
-
-  public abstract static class MediaSession2.SessionCallback {
-    ctor public MediaSession2.SessionCallback();
-    method public void onCommandResult(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object, @NonNull android.media.Session2Command, @NonNull android.media.Session2Command.Result);
-    method @Nullable public android.media.Session2CommandGroup onConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
-    method public void onDisconnected(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
-    method public void onPostConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
-    method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
-  }
-
-  public abstract class MediaSession2Service extends android.app.Service {
-    ctor public MediaSession2Service();
-    method public final void addSession(@NonNull android.media.MediaSession2);
-    method @NonNull public final java.util.List<android.media.MediaSession2> getSessions();
-    method @CallSuper @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
-    method @Nullable public abstract android.media.MediaSession2 onGetSession(@NonNull android.media.MediaSession2.ControllerInfo);
-    method @Nullable public abstract android.media.MediaSession2Service.MediaNotification onUpdateNotification(@NonNull android.media.MediaSession2);
-    method public final void removeSession(@NonNull android.media.MediaSession2);
-    field public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
-  }
-
-  public static class MediaSession2Service.MediaNotification {
-    ctor public MediaSession2Service.MediaNotification(int, @NonNull android.app.Notification);
-    method @NonNull public android.app.Notification getNotification();
-    method public int getNotificationId();
-  }
-
-  public final class Session2Command implements android.os.Parcelable {
-    ctor public Session2Command(int);
-    ctor public Session2Command(@NonNull String, @Nullable android.os.Bundle);
-    method public int describeContents();
-    method public int getCommandCode();
-    method @Nullable public String getCustomAction();
-    method @Nullable public android.os.Bundle getCustomExtras();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2Command> CREATOR;
-  }
-
-  public static final class Session2Command.Result {
-    ctor public Session2Command.Result(int, @Nullable android.os.Bundle);
-    method public int getResultCode();
-    method @Nullable public android.os.Bundle getResultData();
-    field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
-    field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
-    field public static final int RESULT_SUCCESS = 0; // 0x0
-  }
-
-  public final class Session2CommandGroup implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public java.util.Set<android.media.Session2Command> getCommands();
-    method public boolean hasCommand(@NonNull android.media.Session2Command);
-    method public boolean hasCommand(int);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2CommandGroup> CREATOR;
-  }
-
-  public static final class Session2CommandGroup.Builder {
-    ctor public Session2CommandGroup.Builder();
-    ctor public Session2CommandGroup.Builder(@NonNull android.media.Session2CommandGroup);
-    method @NonNull public android.media.Session2CommandGroup.Builder addCommand(@NonNull android.media.Session2Command);
-    method @NonNull public android.media.Session2CommandGroup build();
-    method @NonNull public android.media.Session2CommandGroup.Builder removeCommand(@NonNull android.media.Session2Command);
-  }
-
-  public final class Session2Token implements android.os.Parcelable {
-    ctor public Session2Token(@NonNull android.content.Context, @NonNull android.content.ComponentName);
-    method public int describeContents();
-    method @NonNull public android.os.Bundle getExtras();
-    method @NonNull public String getPackageName();
-    method @Nullable public String getServiceName();
-    method public int getType();
-    method public int getUid();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2Token> CREATOR;
-    field public static final int TYPE_SESSION = 0; // 0x0
-    field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
-  }
-
-}
-
diff --git a/apex/media/framework/api/module-lib-current.txt b/apex/media/framework/api/module-lib-current.txt
deleted file mode 100644
index 7317f14..0000000
--- a/apex/media/framework/api/module-lib-current.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-// Signature format: 2.0
-package android.media {
-
-  public class MediaCommunicationManager {
-    method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaCommunicationManager.SessionCallback);
-    method public void unregisterSessionCallback(@NonNull android.media.MediaCommunicationManager.SessionCallback);
-  }
-
-  public static interface MediaCommunicationManager.SessionCallback {
-    method public default void onSession2TokenCreated(@NonNull android.media.Session2Token);
-    method public default void onSession2TokensChanged(@NonNull java.util.List<android.media.Session2Token>);
-  }
-
-  public class MediaFrameworkInitializer {
-    method public static void registerServiceWrappers();
-    method public static void setMediaServiceManager(@NonNull android.media.MediaServiceManager);
-  }
-
-  @Deprecated public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
-    ctor @Deprecated public MediaParceledListSlice(@NonNull java.util.List<T>);
-    method @Deprecated public int describeContents();
-    method @Deprecated @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList();
-    method @Deprecated public java.util.List<T> getList();
-    method @Deprecated public void setInlineCountLimit(int);
-    method @Deprecated public void writeToParcel(android.os.Parcel, int);
-    field @Deprecated @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR;
-  }
-
-}
-
diff --git a/apex/media/framework/api/module-lib-removed.txt b/apex/media/framework/api/module-lib-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/framework/api/module-lib-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/framework/api/removed.txt b/apex/media/framework/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/framework/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
deleted file mode 100644
index 6eea769..0000000
--- a/apex/media/framework/api/system-current.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-// Signature format: 2.0
-package android.media {
-
-  public final class MediaTranscodingManager {
-    method @Nullable public android.media.MediaTranscodingManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodingManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodingManager.OnTranscodingFinishedListener);
-  }
-
-  @java.lang.FunctionalInterface public static interface MediaTranscodingManager.OnTranscodingFinishedListener {
-    method public void onTranscodingFinished(@NonNull android.media.MediaTranscodingManager.TranscodingSession);
-  }
-
-  public abstract static class MediaTranscodingManager.TranscodingRequest {
-    method public int getClientPid();
-    method public int getClientUid();
-    method @Nullable public android.os.ParcelFileDescriptor getDestinationFileDescriptor();
-    method @NonNull public android.net.Uri getDestinationUri();
-    method @Nullable public android.os.ParcelFileDescriptor getSourceFileDescriptor();
-    method @NonNull public android.net.Uri getSourceUri();
-  }
-
-  public static class MediaTranscodingManager.TranscodingRequest.VideoFormatResolver {
-    ctor public MediaTranscodingManager.TranscodingRequest.VideoFormatResolver(@NonNull android.media.ApplicationMediaCapabilities, @NonNull android.media.MediaFormat);
-    method @Nullable public android.media.MediaFormat resolveVideoFormat();
-    method public boolean shouldTranscode();
-  }
-
-  public static final class MediaTranscodingManager.TranscodingSession {
-    method public boolean addClientUid(int);
-    method public void cancel();
-    method @NonNull public java.util.List<java.lang.Integer> getClientUids();
-    method public int getErrorCode();
-    method @IntRange(from=0, to=100) public int getProgress();
-    method public int getResult();
-    method public int getSessionId();
-    method public int getStatus();
-    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener);
-    field public static final int ERROR_DROPPED_BY_SERVICE = 1; // 0x1
-    field public static final int ERROR_NONE = 0; // 0x0
-    field public static final int ERROR_SERVICE_DIED = 2; // 0x2
-    field public static final int RESULT_CANCELED = 4; // 0x4
-    field public static final int RESULT_ERROR = 3; // 0x3
-    field public static final int RESULT_NONE = 1; // 0x1
-    field public static final int RESULT_SUCCESS = 2; // 0x2
-    field public static final int STATUS_FINISHED = 3; // 0x3
-    field public static final int STATUS_PAUSED = 4; // 0x4
-    field public static final int STATUS_PENDING = 1; // 0x1
-    field public static final int STATUS_RUNNING = 2; // 0x2
-  }
-
-  @java.lang.FunctionalInterface public static interface MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener {
-    method public void onProgressUpdate(@NonNull android.media.MediaTranscodingManager.TranscodingSession, @IntRange(from=0, to=100) int);
-  }
-
-  public static final class MediaTranscodingManager.VideoTranscodingRequest extends android.media.MediaTranscodingManager.TranscodingRequest {
-    method @NonNull public android.media.MediaFormat getVideoTrackFormat();
-  }
-
-  public static final class MediaTranscodingManager.VideoTranscodingRequest.Builder {
-    ctor public MediaTranscodingManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat);
-    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest build();
-    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientPid(int);
-    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientUid(int);
-    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
-    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
-  }
-
-}
-
diff --git a/apex/media/framework/api/system-removed.txt b/apex/media/framework/api/system-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/framework/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/framework/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt
deleted file mode 100644
index 91489dc..0000000
--- a/apex/media/framework/jarjar_rules.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-rule com.android.modules.** android.media.internal.@1
-rule com.google.android.exoplayer2.** android.media.internal.exo.@1
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
deleted file mode 100644
index 97fa0ec..0000000
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.NonNull;
-import android.content.ContentResolver;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import com.android.modules.annotation.MinSdk;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- ApplicationMediaCapabilities is an immutable class that encapsulates an application's capabilities
- for handling newer video codec format and media features.
-
- <p>
- Android 12 introduces Compatible media transcoding feature.  See
- <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding">
- Compatible media transcoding</a>. By default, Android assumes apps can support playback of all
- media formats. Apps that would like to request that media be transcoded into a more compatible
- format should declare their media capabilities in a media_capabilities.xml resource file and add it
- as a property tag in the AndroidManifest.xml file. Here is a example:
- <pre>
- {@code
- <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
-     <format android:name="HEVC" supported="true"/>
-     <format android:name="HDR10" supported="false"/>
-     <format android:name="HDR10Plus" supported="false"/>
- </media-capabilities>
- }
- </pre>
- The ApplicationMediaCapabilities class is generated from this xml and used by the platform to
- represent an application's media capabilities in order to determine whether modern media files need
- to be transcoded for that application.
- </p>
-
- <p>
- ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
- {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
- control over the transcoding that is built into the platform. ApplicationMediaCapabilities
- provided by applications at runtime like this override the default manifest capabilities for that
- media access.The object could be build either through {@link #createFromXml(XmlPullParser)} or
- through the builder class {@link ApplicationMediaCapabilities.Builder}
-
- <h3> Video Codec Support</h3>
- <p>
- Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
- for newer format with this class as they are assumed to support older format like h.264.
-
- <h3>Capability of handling HDR(high dynamic range) video</h3>
- <p>
- There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
- application will only need to specify individual types they supported.
- */
-@MinSdk(Build.VERSION_CODES.S)
-public final class ApplicationMediaCapabilities implements Parcelable {
-    private static final String TAG = "ApplicationMediaCapabilities";
-
-    /** List of supported video codec mime types. */
-    private Set<String> mSupportedVideoMimeTypes = new HashSet<>();
-
-    /** List of unsupported video codec mime types. */
-    private Set<String> mUnsupportedVideoMimeTypes = new HashSet<>();
-
-    /** List of supported hdr types. */
-    private Set<String> mSupportedHdrTypes = new HashSet<>();
-
-    /** List of unsupported hdr types. */
-    private Set<String> mUnsupportedHdrTypes = new HashSet<>();
-
-    private boolean mIsSlowMotionSupported = false;
-
-    private ApplicationMediaCapabilities(Builder b) {
-        mSupportedVideoMimeTypes.addAll(b.getSupportedVideoMimeTypes());
-        mUnsupportedVideoMimeTypes.addAll(b.getUnsupportedVideoMimeTypes());
-        mSupportedHdrTypes.addAll(b.getSupportedHdrTypes());
-        mUnsupportedHdrTypes.addAll(b.getUnsupportedHdrTypes());
-        mIsSlowMotionSupported = b.mIsSlowMotionSupported;
-    }
-
-    /**
-     * Query if a video codec format is supported by the application.
-     * <p>
-     * If the application has not specified supporting the format or not, this will return false.
-     * Use {@link #isFormatSpecified(String)} to query if a format is specified or not.
-     *
-     * @param videoMime The mime type of the video codec format. Must be the one used in
-     * {@link MediaFormat#KEY_MIME}.
-     * @return true if application supports the video codec format, false otherwise.
-     */
-    public boolean isVideoMimeTypeSupported(
-            @NonNull String videoMime) {
-        if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Query if a HDR type is supported by the application.
-     * <p>
-     * If the application has not specified supporting the format or not, this will return false.
-     * Use {@link #isFormatSpecified(String)} to query if a format is specified or not.
-     *
-     * @param hdrType The type of the HDR format.
-     * @return true if application supports the HDR format, false otherwise.
-     */
-    public boolean isHdrTypeSupported(
-            @NonNull @MediaFeature.MediaHdrType String hdrType) {
-        if (mSupportedHdrTypes.contains(hdrType)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Query if a format is specified by the application.
-     * <p>
-     * The format could be either the video format or the hdr format.
-     *
-     * @param format The name of the format.
-     * @return true if application specifies the format, false otherwise.
-     */
-    public boolean isFormatSpecified(@NonNull String format) {
-        if (mSupportedVideoMimeTypes.contains(format) || mUnsupportedVideoMimeTypes.contains(format)
-                || mSupportedHdrTypes.contains(format) || mUnsupportedHdrTypes.contains(format)) {
-            return true;
-
-        }
-        return false;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // Write out the supported video mime types.
-        dest.writeInt(mSupportedVideoMimeTypes.size());
-        for (String cap : mSupportedVideoMimeTypes) {
-            dest.writeString(cap);
-        }
-        // Write out the unsupported video mime types.
-        dest.writeInt(mUnsupportedVideoMimeTypes.size());
-        for (String cap : mUnsupportedVideoMimeTypes) {
-            dest.writeString(cap);
-        }
-        // Write out the supported hdr types.
-        dest.writeInt(mSupportedHdrTypes.size());
-        for (String cap : mSupportedHdrTypes) {
-            dest.writeString(cap);
-        }
-        // Write out the unsupported hdr types.
-        dest.writeInt(mUnsupportedHdrTypes.size());
-        for (String cap : mUnsupportedHdrTypes) {
-            dest.writeString(cap);
-        }
-        // Write out the supported slow motion.
-        dest.writeBoolean(mIsSlowMotionSupported);
-    }
-
-    @Override
-    public String toString() {
-        String caps = new String(
-                "Supported Video MimeTypes: " + mSupportedVideoMimeTypes.toString());
-        caps += "Unsupported Video MimeTypes: " + mUnsupportedVideoMimeTypes.toString();
-        caps += "Supported HDR types: " + mSupportedHdrTypes.toString();
-        caps += "Unsupported HDR types: " + mUnsupportedHdrTypes.toString();
-        caps += "Supported slow motion: " + mIsSlowMotionSupported;
-        return caps;
-    }
-
-    @NonNull
-    public static final Creator<ApplicationMediaCapabilities> CREATOR =
-            new Creator<ApplicationMediaCapabilities>() {
-                public ApplicationMediaCapabilities createFromParcel(Parcel in) {
-                    ApplicationMediaCapabilities.Builder builder =
-                            new ApplicationMediaCapabilities.Builder();
-
-                    // Parse supported video codec mime types.
-                    int count = in.readInt();
-                    for (int readCount = 0; readCount < count; ++readCount) {
-                        builder.addSupportedVideoMimeType(in.readString());
-                    }
-
-                    // Parse unsupported video codec mime types.
-                    count = in.readInt();
-                    for (int readCount = 0; readCount < count; ++readCount) {
-                        builder.addUnsupportedVideoMimeType(in.readString());
-                    }
-
-                    // Parse supported hdr types.
-                    count = in.readInt();
-                    for (int readCount = 0; readCount < count; ++readCount) {
-                        builder.addSupportedHdrType(in.readString());
-                    }
-
-                    // Parse unsupported hdr types.
-                    count = in.readInt();
-                    for (int readCount = 0; readCount < count; ++readCount) {
-                        builder.addUnsupportedHdrType(in.readString());
-                    }
-
-                    boolean supported = in.readBoolean();
-                    builder.setSlowMotionSupported(supported);
-
-                    return builder.build();
-                }
-
-                public ApplicationMediaCapabilities[] newArray(int size) {
-                    return new ApplicationMediaCapabilities[size];
-                }
-            };
-
-    /**
-     * Query the video codec mime types supported by the application.
-     * @return List of supported video codec mime types. The list will be empty if there are none.
-     */
-    @NonNull
-    public List<String> getSupportedVideoMimeTypes() {
-        return new ArrayList<>(mSupportedVideoMimeTypes);
-    }
-
-    /**
-     * Query the video codec mime types that are not supported by the application.
-     * @return List of unsupported video codec mime types. The list will be empty if there are none.
-     */
-    @NonNull
-    public List<String> getUnsupportedVideoMimeTypes() {
-        return new ArrayList<>(mUnsupportedVideoMimeTypes);
-    }
-
-    /**
-     * Query all hdr types that are supported by the application.
-     * @return List of supported hdr types. The list will be empty if there are none.
-     */
-    @NonNull
-    public List<String> getSupportedHdrTypes() {
-        return new ArrayList<>(mSupportedHdrTypes);
-    }
-
-    /**
-     * Query all hdr types that are not supported by the application.
-     * @return List of unsupported hdr types. The list will be empty if there are none.
-     */
-    @NonNull
-    public List<String> getUnsupportedHdrTypes()  {
-        return new ArrayList<>(mUnsupportedHdrTypes);
-    }
-
-    /**
-     * Whether handling of slow-motion video is supported
-     * @hide
-     */
-    public boolean isSlowMotionSupported() {
-        return mIsSlowMotionSupported;
-    }
-
-    /**
-     * Creates {@link ApplicationMediaCapabilities} from an xml.
-     *
-     * The xml's syntax is the same as the media_capabilities.xml used by the AndroidManifest.xml.
-     * <p> Here is an example:
-     *
-     * <pre>
-     * {@code
-     * <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
-     *     <format android:name="HEVC" supported="true"/>
-     *     <format android:name="HDR10" supported="false"/>
-     *     <format android:name="HDR10Plus" supported="false"/>
-     * </media-capabilities>
-     * }
-     * </pre>
-     * <p>
-     *
-     * @param xmlParser The underlying {@link XmlPullParser} that will read the xml.
-     * @return An ApplicationMediaCapabilities object.
-     * @throws UnsupportedOperationException if the capabilities in xml config are invalid or
-     * incompatible.
-     */
-    // TODO: Add developer.android.com link for the format of the xml.
-    @NonNull
-    public static ApplicationMediaCapabilities createFromXml(@NonNull XmlPullParser xmlParser) {
-        ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder();
-        builder.parseXml(xmlParser);
-        return builder.build();
-    }
-
-    /**
-     * Builder class for {@link ApplicationMediaCapabilities} objects.
-     * Use this class to configure and create an ApplicationMediaCapabilities instance. Builder
-     * could be created from an existing ApplicationMediaCapabilities object, from a xml file or
-     * MediaCodecList.
-     * //TODO(hkuang): Add xml parsing support to the builder.
-     */
-    public final static class Builder {
-        /** List of supported video codec mime types. */
-        private Set<String> mSupportedVideoMimeTypes = new HashSet<>();
-
-        /** List of supported hdr types. */
-        private Set<String> mSupportedHdrTypes = new HashSet<>();
-
-        /** List of unsupported video codec mime types. */
-        private Set<String> mUnsupportedVideoMimeTypes = new HashSet<>();
-
-        /** List of unsupported hdr types. */
-        private Set<String> mUnsupportedHdrTypes = new HashSet<>();
-
-        private boolean mIsSlowMotionSupported = false;
-
-        /* Map to save the format read from the xml. */
-        private Map<String, Boolean> mFormatSupportedMap =  new HashMap<String, Boolean>();
-
-        /**
-         * Constructs a new Builder with all the supports default to false.
-         */
-        public Builder() {
-        }
-
-        private void parseXml(@NonNull XmlPullParser xmlParser)
-                throws UnsupportedOperationException {
-            if (xmlParser == null) {
-                throw new IllegalArgumentException("XmlParser must not be null");
-            }
-
-            try {
-                while (xmlParser.next() != XmlPullParser.START_TAG) {
-                    continue;
-                }
-
-                // Validates the tag is "media-capabilities".
-                if (!xmlParser.getName().equals("media-capabilities")) {
-                    throw new UnsupportedOperationException("Invalid tag");
-                }
-
-                xmlParser.next();
-                while (xmlParser.getEventType() != XmlPullParser.END_TAG) {
-                    while (xmlParser.getEventType() != XmlPullParser.START_TAG) {
-                        if (xmlParser.getEventType() == XmlPullParser.END_DOCUMENT) {
-                            return;
-                        }
-                        xmlParser.next();
-                    }
-
-                    // Validates the tag is "format".
-                    if (xmlParser.getName().equals("format")) {
-                        parseFormatTag(xmlParser);
-                    } else {
-                        throw new UnsupportedOperationException("Invalid tag");
-                    }
-                    while (xmlParser.getEventType() != XmlPullParser.END_TAG) {
-                        xmlParser.next();
-                    }
-                    xmlParser.next();
-                }
-            } catch (XmlPullParserException xppe) {
-                throw new UnsupportedOperationException("Ill-formatted xml file");
-            } catch (java.io.IOException ioe) {
-                throw new UnsupportedOperationException("Unable to read xml file");
-            }
-        }
-
-        private void parseFormatTag(XmlPullParser xmlParser) {
-            String name = null;
-            String supported = null;
-            for (int i = 0; i < xmlParser.getAttributeCount(); i++) {
-                String attrName = xmlParser.getAttributeName(i);
-                if (attrName.equals("name")) {
-                    name = xmlParser.getAttributeValue(i);
-                } else if (attrName.equals("supported")) {
-                    supported = xmlParser.getAttributeValue(i);
-                } else {
-                    throw new UnsupportedOperationException("Invalid attribute name " + attrName);
-                }
-            }
-
-            if (name != null && supported != null) {
-                if (!supported.equals("true") && !supported.equals("false")) {
-                    throw new UnsupportedOperationException(
-                            ("Supported value must be either true or false"));
-                }
-                boolean isSupported = Boolean.parseBoolean(supported);
-
-                // Check if the format is already found before.
-                if (mFormatSupportedMap.get(name) != null && mFormatSupportedMap.get(name)
-                        != isSupported) {
-                    throw new UnsupportedOperationException(
-                            "Format: " + name + " has conflict supported value");
-                }
-
-                switch (name) {
-                    case "HEVC":
-                        if (isSupported) {
-                            mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
-                        } else {
-                            mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
-                        }
-                        break;
-                    case "VP9":
-                        if (isSupported) {
-                            mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_VP9);
-                        } else {
-                            mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_VP9);
-                        }
-                        break;
-                    case "AV1":
-                        if (isSupported) {
-                            mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_AV1);
-                        } else {
-                            mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_AV1);
-                        }
-                        break;
-                    case "HDR10":
-                        if (isSupported) {
-                            mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10);
-                        } else {
-                            mUnsupportedHdrTypes.add(MediaFeature.HdrType.HDR10);
-                        }
-                        break;
-                    case "HDR10Plus":
-                        if (isSupported) {
-                            mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS);
-                        } else {
-                            mUnsupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS);
-                        }
-                        break;
-                    case "Dolby-Vision":
-                        if (isSupported) {
-                            mSupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION);
-                        } else {
-                            mUnsupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION);
-                        }
-                        break;
-                    case "HLG":
-                        if (isSupported) {
-                            mSupportedHdrTypes.add(MediaFeature.HdrType.HLG);
-                        } else {
-                            mUnsupportedHdrTypes.add(MediaFeature.HdrType.HLG);
-                        }
-                        break;
-                    case "SlowMotion":
-                        mIsSlowMotionSupported = isSupported;
-                        break;
-                    default:
-                        Log.w(TAG, "Invalid format name " + name);
-                }
-                // Save the name and isSupported into the map for validate later.
-                mFormatSupportedMap.put(name, isSupported);
-            } else {
-                throw new UnsupportedOperationException(
-                        "Format name and supported must both be specified");
-            }
-        }
-
-        /**
-         * Builds a {@link ApplicationMediaCapabilities} object.
-         *
-         * @return a new {@link ApplicationMediaCapabilities} instance successfully initialized
-         * with all the parameters set on this <code>Builder</code>.
-         * @throws UnsupportedOperationException if the parameters set on the
-         *                                       <code>Builder</code> were incompatible, or if they
-         *                                       are not supported by the
-         *                                       device.
-         */
-        @NonNull
-        public ApplicationMediaCapabilities build() {
-            Log.d(TAG,
-                    "Building ApplicationMediaCapabilities with: (Supported HDR: "
-                            + mSupportedHdrTypes.toString() + " Unsupported HDR: "
-                            + mUnsupportedHdrTypes.toString() + ") (Supported Codec: "
-                            + " " + mSupportedVideoMimeTypes.toString() + " Unsupported Codec:"
-                            + mUnsupportedVideoMimeTypes.toString() + ") "
-                            + mIsSlowMotionSupported);
-
-            // If hdr is supported, application must also support hevc.
-            if (!mSupportedHdrTypes.isEmpty() && !mSupportedVideoMimeTypes.contains(
-                    MediaFormat.MIMETYPE_VIDEO_HEVC)) {
-                throw new UnsupportedOperationException("Only support HEVC mime type");
-            }
-            return new ApplicationMediaCapabilities(this);
-        }
-
-        /**
-         * Adds a supported video codec mime type.
-         *
-         * @param codecMime Supported codec mime types. Must be one of the mime type defined
-         *                  in {@link MediaFormat}.
-         * @throws IllegalArgumentException if mime type is not valid.
-         */
-        @NonNull
-        public Builder addSupportedVideoMimeType(
-                @NonNull String codecMime) {
-            mSupportedVideoMimeTypes.add(codecMime);
-            return this;
-        }
-
-        private List<String> getSupportedVideoMimeTypes() {
-            return new ArrayList<>(mSupportedVideoMimeTypes);
-        }
-
-        private boolean isValidVideoCodecMimeType(@NonNull String codecMime) {
-            if (!codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)
-                    && !codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9)
-                    && !codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1)) {
-                return false;
-            }
-            return true;
-        }
-
-        /**
-         * Adds an unsupported video codec mime type.
-         *
-         * @param codecMime Unsupported codec mime type. Must be one of the mime type defined
-         *                  in {@link MediaFormat}.
-         * @throws IllegalArgumentException if mime type is not valid.
-         */
-        @NonNull
-        public Builder addUnsupportedVideoMimeType(
-                @NonNull String codecMime) {
-            if (!isValidVideoCodecMimeType(codecMime)) {
-                throw new IllegalArgumentException("Invalid codec mime type: " + codecMime);
-            }
-            mUnsupportedVideoMimeTypes.add(codecMime);
-            return this;
-        }
-
-        private List<String> getUnsupportedVideoMimeTypes() {
-            return new ArrayList<>(mUnsupportedVideoMimeTypes);
-        }
-
-        /**
-         * Adds a supported hdr type.
-         *
-         * @param hdrType Supported hdr type. Must be one of the String defined in
-         *                {@link MediaFeature.HdrType}.
-         * @throws IllegalArgumentException if hdrType is not valid.
-         */
-        @NonNull
-        public Builder addSupportedHdrType(
-                @NonNull @MediaFeature.MediaHdrType String hdrType) {
-            if (!isValidVideoCodecHdrType(hdrType)) {
-                throw new IllegalArgumentException("Invalid hdr type: " + hdrType);
-            }
-            mSupportedHdrTypes.add(hdrType);
-            return this;
-        }
-
-        private List<String> getSupportedHdrTypes() {
-            return new ArrayList<>(mSupportedHdrTypes);
-        }
-
-        private boolean isValidVideoCodecHdrType(@NonNull String hdrType) {
-            if (!hdrType.equals(MediaFeature.HdrType.DOLBY_VISION)
-                    && !hdrType.equals(MediaFeature.HdrType.HDR10)
-                    && !hdrType.equals(MediaFeature.HdrType.HDR10_PLUS)
-                    && !hdrType.equals(MediaFeature.HdrType.HLG)) {
-                return false;
-            }
-            return true;
-        }
-
-        /**
-         * Adds an unsupported hdr type.
-         *
-         * @param hdrType Unsupported hdr type. Must be one of the String defined in
-         *                {@link MediaFeature.HdrType}.
-         * @throws IllegalArgumentException if hdrType is not valid.
-         */
-        @NonNull
-        public Builder addUnsupportedHdrType(
-                @NonNull @MediaFeature.MediaHdrType String hdrType) {
-            if (!isValidVideoCodecHdrType(hdrType)) {
-                throw new IllegalArgumentException("Invalid hdr type: " + hdrType);
-            }
-            mUnsupportedHdrTypes.add(hdrType);
-            return this;
-        }
-
-        private List<String> getUnsupportedHdrTypes() {
-            return new ArrayList<>(mUnsupportedHdrTypes);
-        }
-
-        /**
-         * Sets whether slow-motion video is supported.
-         * If an application indicates support for slow-motion, it is application's responsibility
-         * to parse the slow-motion videos using their own parser or using support library.
-         * @see android.media.MediaFormat#KEY_SLOW_MOTION_MARKERS
-         * @hide
-         */
-        @NonNull
-        public Builder setSlowMotionSupported(boolean slowMotionSupported) {
-            mIsSlowMotionSupported = slowMotionSupported;
-            return this;
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java b/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
deleted file mode 100644
index 915f3f6a3..0000000
--- a/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This is a copied version of BaseParceledListSlice in framework with hidden API usages
- * removed.
- *
- * Transfer a large list of Parcelable objects across an IPC.  Splits into
- * multiple transactions if needed.
- *
- * Caveat: for efficiency and security, all elements must be the same concrete type.
- * In order to avoid writing the class name of each object, we must ensure that
- * each object is the same type, or else unparceling then reparceling the data may yield
- * a different result if the class name encoded in the Parcelable is a Base type.
- * See b/17671747.
- *
- * @hide
- */
-abstract class BaseMediaParceledListSlice<T> implements Parcelable {
-    private static String TAG = "BaseMediaParceledListSlice";
-    private static final boolean DEBUG = false;
-
-    /*
-     * TODO get this number from somewhere else. For now set it to a quarter of
-     * the 1MB limit.
-     */
-    // private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes();
-    private static final int MAX_IPC_SIZE = 64 * 1024;
-
-    private final List<T> mList;
-
-    private int mInlineCountLimit = Integer.MAX_VALUE;
-
-    public BaseMediaParceledListSlice(List<T> list) {
-        mList = list;
-    }
-
-    @SuppressWarnings("unchecked")
-    BaseMediaParceledListSlice(Parcel p, ClassLoader loader) {
-        final int N = p.readInt();
-        mList = new ArrayList<T>(N);
-        if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
-        if (N <= 0) {
-            return;
-        }
-
-        Parcelable.Creator<?> creator = readParcelableCreator(p, loader);
-        Class<?> listElementClass = null;
-
-        int i = 0;
-        while (i < N) {
-            if (p.readInt() == 0) {
-                break;
-            }
-
-            final T parcelable = readCreator(creator, p, loader);
-            if (listElementClass == null) {
-                listElementClass = parcelable.getClass();
-            } else {
-                verifySameType(listElementClass, parcelable.getClass());
-            }
-
-            mList.add(parcelable);
-
-            if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
-            i++;
-        }
-        if (i >= N) {
-            return;
-        }
-        final IBinder retriever = p.readStrongBinder();
-        while (i < N) {
-            if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
-            Parcel data = Parcel.obtain();
-            Parcel reply = Parcel.obtain();
-            data.writeInt(i);
-            try {
-                retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
-                return;
-            }
-            while (i < N && reply.readInt() != 0) {
-                final T parcelable = readCreator(creator, reply, loader);
-                verifySameType(listElementClass, parcelable.getClass());
-
-                mList.add(parcelable);
-
-                if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
-                i++;
-            }
-            reply.recycle();
-            data.recycle();
-        }
-    }
-
-    private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) {
-        if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
-            Parcelable.ClassLoaderCreator<?> classLoaderCreator =
-                    (Parcelable.ClassLoaderCreator<?>) creator;
-            return (T) classLoaderCreator.createFromParcel(p, loader);
-        }
-        return (T) creator.createFromParcel(p);
-    }
-
-    private static void verifySameType(final Class<?> expected, final Class<?> actual) {
-        if (!actual.equals(expected)) {
-            throw new IllegalArgumentException("Can't unparcel type "
-                    + (actual == null ? null : actual.getName()) + " in list of type "
-                    + (expected == null ? null : expected.getName()));
-        }
-    }
-
-    public List<T> getList() {
-        return mList;
-    }
-
-    /**
-     * Set a limit on the maximum number of entries in the array that will be included
-     * inline in the initial parcelling of this object.
-     */
-    public void setInlineCountLimit(int maxCount) {
-        mInlineCountLimit = maxCount;
-    }
-
-    /**
-     * Write this to another Parcel. Note that this discards the internal Parcel
-     * and should not be used anymore. This is so we can pass this to a Binder
-     * where we won't have a chance to call recycle on this.
-     */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        final int N = mList.size();
-        final int callFlags = flags;
-        dest.writeInt(N);
-        if (DEBUG) Log.d(TAG, "Writing " + N + " items");
-        if (N > 0) {
-            final Class<?> listElementClass = mList.get(0).getClass();
-            writeParcelableCreator(mList.get(0), dest);
-            int i = 0;
-            while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
-                dest.writeInt(1);
-
-                final T parcelable = mList.get(i);
-                verifySameType(listElementClass, parcelable.getClass());
-                writeElement(parcelable, dest, callFlags);
-
-                if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
-                i++;
-            }
-            if (i < N) {
-                dest.writeInt(0);
-                Binder retriever = new Binder() {
-                    @Override
-                    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
-                            throws RemoteException {
-                        if (code != FIRST_CALL_TRANSACTION) {
-                            return super.onTransact(code, data, reply, flags);
-                        }
-                        int i = data.readInt();
-                        if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
-                        while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
-                            reply.writeInt(1);
-
-                            final T parcelable = mList.get(i);
-                            verifySameType(listElementClass, parcelable.getClass());
-                            writeElement(parcelable, reply, callFlags);
-
-                            if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
-                            i++;
-                        }
-                        if (i < N) {
-                            if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
-                            reply.writeInt(0);
-                        }
-                        return true;
-                    }
-                };
-                if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
-                dest.writeStrongBinder(retriever);
-            }
-        }
-    }
-
-    abstract void writeElement(T parcelable, Parcel reply, int callFlags);
-
-    abstract void writeParcelableCreator(T parcelable, Parcel dest);
-
-    abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader);
-}
diff --git a/apex/media/framework/java/android/media/BufferingParams.java b/apex/media/framework/java/android/media/BufferingParams.java
deleted file mode 100644
index 04af028..0000000
--- a/apex/media/framework/java/android/media/BufferingParams.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Structure for source buffering management params.
- *
- * Used by {@link MediaPlayer#getBufferingParams()} and
- * {@link MediaPlayer#setBufferingParams(BufferingParams)}
- * to control source buffering behavior.
- *
- * <p>There are two stages of source buffering in {@link MediaPlayer}: initial buffering
- * (when {@link MediaPlayer} is being prepared) and rebuffering (when {@link MediaPlayer}
- * is playing back source). {@link BufferingParams} includes corresponding marks for each
- * stage of source buffering. The marks are time based (in milliseconds).
- *
- * <p>{@link MediaPlayer} source component has default marks which can be queried by
- * calling {@link MediaPlayer#getBufferingParams()} before any change is made by
- * {@link MediaPlayer#setBufferingParams()}.
- * <ul>
- * <li><strong>initial buffering:</strong> initialMarkMs is used when
- * {@link MediaPlayer} is being prepared. When cached data amount exceeds this mark
- * {@link MediaPlayer} is prepared. </li>
- * <li><strong>rebuffering during playback:</strong> resumePlaybackMarkMs is used when
- * {@link MediaPlayer} is playing back content.
- * <ul>
- * <li> {@link MediaPlayer} has internal mark, namely pausePlaybackMarkMs, to decide when
- * to pause playback if cached data amount runs low. This internal mark varies based on
- * type of data source. </li>
- * <li> When cached data amount exceeds resumePlaybackMarkMs, {@link MediaPlayer} will
- * resume playback if it has been paused due to low cached data amount. The internal mark
- * pausePlaybackMarkMs shall be less than resumePlaybackMarkMs. </li>
- * <li> {@link MediaPlayer} has internal mark, namely pauseRebufferingMarkMs, to decide
- * when to pause rebuffering. Apparently, this internal mark shall be no less than
- * resumePlaybackMarkMs. </li>
- * <li> {@link MediaPlayer} has internal mark, namely resumeRebufferingMarkMs, to decide
- * when to resume buffering. This internal mark varies based on type of data source. This
- * mark shall be larger than pausePlaybackMarkMs, and less than pauseRebufferingMarkMs.
- * </li>
- * </ul> </li>
- * </ul>
- * <p>Users should use {@link Builder} to change {@link BufferingParams}.
- * @hide
- */
-public final class BufferingParams implements Parcelable {
-    private static final int BUFFERING_NO_MARK = -1;
-
-    // params
-    private int mInitialMarkMs = BUFFERING_NO_MARK;
-
-    private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;
-
-    private BufferingParams() {
-    }
-
-    /**
-     * Return initial buffering mark in milliseconds.
-     * @return initial buffering mark in milliseconds
-     */
-    public int getInitialMarkMs() {
-        return mInitialMarkMs;
-    }
-
-    /**
-     * Return the mark in milliseconds for resuming playback.
-     * @return the mark for resuming playback in milliseconds
-     */
-    public int getResumePlaybackMarkMs() {
-        return mResumePlaybackMarkMs;
-    }
-
-    /**
-     * Builder class for {@link BufferingParams} objects.
-     * <p> Here is an example where <code>Builder</code> is used to define the
-     * {@link BufferingParams} to be used by a {@link MediaPlayer} instance:
-     *
-     * <pre class="prettyprint">
-     * BufferingParams myParams = mediaplayer.getDefaultBufferingParams();
-     * myParams = new BufferingParams.Builder(myParams)
-     *         .setInitialMarkMs(10000)
-     *         .setResumePlaybackMarkMs(15000)
-     *         .build();
-     * mediaplayer.setBufferingParams(myParams);
-     * </pre>
-     */
-    public static class Builder {
-        private int mInitialMarkMs = BUFFERING_NO_MARK;
-        private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;
-
-        /**
-         * Constructs a new Builder with the defaults.
-         * By default, all marks are -1.
-         */
-        public Builder() {
-        }
-
-        /**
-         * Constructs a new Builder from a given {@link BufferingParams} instance
-         * @param bp the {@link BufferingParams} object whose data will be reused
-         * in the new Builder.
-         */
-        public Builder(BufferingParams bp) {
-            mInitialMarkMs = bp.mInitialMarkMs;
-            mResumePlaybackMarkMs = bp.mResumePlaybackMarkMs;
-        }
-
-        /**
-         * Combines all of the fields that have been set and return a new
-         * {@link BufferingParams} object. <code>IllegalStateException</code> will be
-         * thrown if there is conflict between fields.
-         * @return a new {@link BufferingParams} object
-         */
-        public BufferingParams build() {
-            BufferingParams bp = new BufferingParams();
-            bp.mInitialMarkMs = mInitialMarkMs;
-            bp.mResumePlaybackMarkMs = mResumePlaybackMarkMs;
-
-            return bp;
-        }
-
-        /**
-         * Sets the time based mark in milliseconds for initial buffering.
-         * @param markMs time based mark in milliseconds
-         * @return the same Builder instance.
-         */
-        public Builder setInitialMarkMs(int markMs) {
-            mInitialMarkMs = markMs;
-            return this;
-        }
-
-        /**
-         * Sets the time based mark in milliseconds for resuming playback.
-         * @param markMs time based mark in milliseconds for resuming playback
-         * @return the same Builder instance.
-         */
-        public Builder setResumePlaybackMarkMs(int markMs) {
-            mResumePlaybackMarkMs = markMs;
-            return this;
-        }
-    }
-
-    private BufferingParams(Parcel in) {
-        mInitialMarkMs = in.readInt();
-        mResumePlaybackMarkMs = in.readInt();
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BufferingParams> CREATOR =
-            new Parcelable.Creator<BufferingParams>() {
-                @Override
-                public BufferingParams createFromParcel(Parcel in) {
-                    return new BufferingParams(in);
-                }
-
-                @Override
-                public BufferingParams[] newArray(int size) {
-                    return new BufferingParams[size];
-                }
-            };
-
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mInitialMarkMs);
-        dest.writeInt(mResumePlaybackMarkMs);
-    }
-}
diff --git a/apex/media/framework/java/android/media/Controller2Link.java b/apex/media/framework/java/android/media/Controller2Link.java
deleted file mode 100644
index 8eefec7..0000000
--- a/apex/media/framework/java/android/media/Controller2Link.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-
-import java.util.Objects;
-
-/**
- * Handles incoming commands from {@link MediaSession2} to {@link MediaController2}.
- * @hide
- */
-// @SystemApi
-public final class Controller2Link implements Parcelable {
-    private static final String TAG = "Controller2Link";
-    private static final boolean DEBUG = MediaController2.DEBUG;
-
-    public static final @android.annotation.NonNull Parcelable.Creator<Controller2Link> CREATOR =
-            new Parcelable.Creator<Controller2Link>() {
-                @Override
-                public Controller2Link createFromParcel(Parcel in) {
-                    return new Controller2Link(in);
-                }
-
-                @Override
-                public Controller2Link[] newArray(int size) {
-                    return new Controller2Link[size];
-                }
-            };
-
-
-    private final MediaController2 mController;
-    private final IMediaController2 mIController;
-
-    public Controller2Link(MediaController2 controller) {
-        mController = controller;
-        mIController = new Controller2Stub();
-    }
-
-    Controller2Link(Parcel in) {
-        mController = null;
-        mIController = IMediaController2.Stub.asInterface(in.readStrongBinder());
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStrongBinder(mIController.asBinder());
-    }
-
-    @Override
-    public int hashCode() {
-        return mIController.asBinder().hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof Controller2Link)) {
-            return false;
-        }
-        Controller2Link other = (Controller2Link) obj;
-        return Objects.equals(mIController.asBinder(), other.mIController.asBinder());
-    }
-
-    /** Interface method for IMediaController2.notifyConnected */
-    public void notifyConnected(int seq, Bundle connectionResult) {
-        try {
-            mIController.notifyConnected(seq, connectionResult);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaController2.notifyDisonnected */
-    public void notifyDisconnected(int seq) {
-        try {
-            mIController.notifyDisconnected(seq);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaController2.notifyPlaybackActiveChanged */
-    public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) {
-        try {
-            mIController.notifyPlaybackActiveChanged(seq, playbackActive);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaController2.sendSessionCommand */
-    public void sendSessionCommand(int seq, Session2Command command, Bundle args,
-            ResultReceiver resultReceiver) {
-        try {
-            mIController.sendSessionCommand(seq, command, args, resultReceiver);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaController2.cancelSessionCommand */
-    public void cancelSessionCommand(int seq) {
-        try {
-            mIController.cancelSessionCommand(seq);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Stub implementation for IMediaController2.notifyConnected */
-    public void onConnected(int seq, Bundle connectionResult) {
-        if (connectionResult == null) {
-            onDisconnected(seq);
-            return;
-        }
-        mController.onConnected(seq, connectionResult);
-    }
-
-    /** Stub implementation for IMediaController2.notifyDisonnected */
-    public void onDisconnected(int seq) {
-        mController.onDisconnected(seq);
-    }
-
-    /** Stub implementation for IMediaController2.notifyPlaybackActiveChanged */
-    public void onPlaybackActiveChanged(int seq, boolean playbackActive) {
-        mController.onPlaybackActiveChanged(seq, playbackActive);
-    }
-
-    /** Stub implementation for IMediaController2.sendSessionCommand */
-    public void onSessionCommand(int seq, Session2Command command, Bundle args,
-            ResultReceiver resultReceiver) {
-        mController.onSessionCommand(seq, command, args, resultReceiver);
-    }
-
-    /** Stub implementation for IMediaController2.cancelSessionCommand */
-    public void onCancelCommand(int seq) {
-        mController.onCancelCommand(seq);
-    }
-
-    private class Controller2Stub extends IMediaController2.Stub {
-        @Override
-        public void notifyConnected(int seq, Bundle connectionResult) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Controller2Link.this.onConnected(seq, connectionResult);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void notifyDisconnected(int seq) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Controller2Link.this.onDisconnected(seq);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Controller2Link.this.onPlaybackActiveChanged(seq, playbackActive);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void sendSessionCommand(int seq, Session2Command command, Bundle args,
-                ResultReceiver resultReceiver) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void cancelSessionCommand(int seq) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Controller2Link.this.onCancelCommand(seq);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/DataSourceCallback.java b/apex/media/framework/java/android/media/DataSourceCallback.java
deleted file mode 100644
index c297ecd..0000000
--- a/apex/media/framework/java/android/media/DataSourceCallback.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package android.media;
-
-import android.annotation.NonNull;
-
-import java.io.Closeable;
-import java.io.IOException;
-
-/**
- * For supplying media data to the framework. Implement this if your app has
- * special requirements for the way media data is obtained.
- *
- * <p class="note">Methods of this interface may be called on multiple different
- * threads. There will be a thread synchronization point between each call to ensure that
- * modifications to the state of your DataSourceCallback are visible to future calls. This means
- * you don't need to do your own synchronization unless you're modifying the
- * DataSourceCallback from another thread while it's being used by the framework.</p>
- *
- * @hide
- */
-public abstract class DataSourceCallback implements Closeable {
-
-    public static final int END_OF_STREAM = -1;
-
-    /**
-     * Called to request data from the given position.
-     *
-     * Implementations should should write up to {@code size} bytes into
-     * {@code buffer}, and return the number of bytes written.
-     *
-     * Return {@code 0} if size is zero (thus no bytes are read).
-     *
-     * Return {@code -1} to indicate that end of stream is reached.
-     *
-     * @param position the position in the data source to read from.
-     * @param buffer the buffer to read the data into.
-     * @param offset the offset within buffer to read the data into.
-     * @param size the number of bytes to read.
-     * @throws IOException on fatal errors.
-     * @return the number of bytes read, or {@link #END_OF_STREAM} if end of stream is reached.
-     */
-    public abstract int readAt(long position, @NonNull byte[] buffer, int offset, int size)
-            throws IOException;
-
-    /**
-     * Called to get the size of the data source.
-     *
-     * @throws IOException on fatal errors
-     * @return the size of data source in bytes, or -1 if the size is unknown.
-     */
-    public abstract long getSize() throws IOException;
-}
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
deleted file mode 100644
index ef5552e..0000000
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media;
-
-import static android.Manifest.permission.MEDIA_CONTENT_CONTROL;
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.annotation.SystemService;
-import android.content.Context;
-import android.media.session.MediaSession;
-import android.media.session.MediaSessionManager;
-import android.os.Build;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.service.media.MediaBrowserService;
-import android.util.Log;
-import android.view.KeyEvent;
-
-import androidx.annotation.RequiresApi;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.modules.annotation.MinSdk;
-import com.android.modules.utils.build.SdkLevel;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Executor;
-
-/**
- * Provides support for interacting with {@link android.media.MediaSession2 MediaSession2s}
- * that applications have published to express their ongoing media playback state.
- */
-@MinSdk(Build.VERSION_CODES.S)
-@RequiresApi(Build.VERSION_CODES.S)
-@SystemService(Context.MEDIA_COMMUNICATION_SERVICE)
-public class MediaCommunicationManager {
-    private static final String TAG = "MediaCommunicationManager";
-
-    /**
-     * The manager version used from beginning.
-     */
-    private static final int VERSION_1 = 1;
-
-    /**
-     * Current manager version.
-     */
-    private static final int CURRENT_VERSION = VERSION_1;
-
-    private final Context mContext;
-    // Do not access directly use getService().
-    private IMediaCommunicationService mService;
-
-    private final Object mLock = new Object();
-    private final CopyOnWriteArrayList<SessionCallbackRecord> mTokenCallbackRecords =
-            new CopyOnWriteArrayList<>();
-
-    @GuardedBy("mLock")
-    private MediaCommunicationServiceCallbackStub mCallbackStub;
-
-    // TODO: remove this when MCS implements dispatchMediaKeyEvent.
-    private MediaSessionManager mMediaSessionManager;
-
-    /**
-     * @hide
-     */
-    public MediaCommunicationManager(@NonNull Context context) {
-        if (!SdkLevel.isAtLeastS()) {
-            throw new UnsupportedOperationException("Android version must be S or greater.");
-        }
-        mContext = context;
-    }
-
-    /**
-     * Gets the version of this {@link MediaCommunicationManager}.
-     */
-    public @IntRange(from = 1) int getVersion() {
-        return CURRENT_VERSION;
-    }
-
-    /**
-     * Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is
-     * created.
-     * @param token newly created session2 token
-     * @hide
-     */
-    public void notifySession2Created(@NonNull Session2Token token) {
-        Objects.requireNonNull(token, "token shouldn't be null");
-        if (token.getType() != Session2Token.TYPE_SESSION) {
-            throw new IllegalArgumentException("token's type should be TYPE_SESSION");
-        }
-        try {
-            getService().notifySession2Created(token);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Checks whether the remote user is a trusted app.
-     * <p>
-     * An app is trusted if the app holds the
-     * {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission or has an enabled
-     * notification listener.
-     *
-     * @param userInfo The remote user info from either
-     *            {@link MediaSession#getCurrentControllerInfo()} or
-     *            {@link MediaBrowserService#getCurrentBrowserInfo()}.
-     * @return {@code true} if the remote user is trusted or {@code false} otherwise.
-     * @hide
-     */
-    public boolean isTrustedForMediaControl(@NonNull MediaSessionManager.RemoteUserInfo userInfo) {
-        Objects.requireNonNull(userInfo, "userInfo shouldn't be null");
-        if (userInfo.getPackageName() == null) {
-            return false;
-        }
-        try {
-            return getService().isTrusted(
-                    userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid());
-        } catch (RemoteException e) {
-            Log.w(TAG, "Cannot communicate with the service.", e);
-        }
-        return false;
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the
-     * current user.
-     * <p>
-     * Although this API can be used without any restriction, each session owners can accept or
-     * reject your uses of {@link MediaSession2}.
-     *
-     * @return A list of {@link Session2Token}.
-     */
-    @NonNull
-    public List<Session2Token> getSession2Tokens() {
-        return getSession2Tokens(UserHandle.myUserId());
-    }
-
-    /**
-     * Adds a callback to be notified when the list of active sessions changes.
-     * <p>
-     * This requires the {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be
-     * held by the calling app.
-     * </p>
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    @RequiresPermission(MEDIA_CONTENT_CONTROL)
-    public void registerSessionCallback(@CallbackExecutor @NonNull Executor executor,
-            @NonNull SessionCallback callback) {
-        Objects.requireNonNull(executor, "executor must not be null");
-        Objects.requireNonNull(callback, "callback must not be null");
-
-        if (!mTokenCallbackRecords.addIfAbsent(
-                new SessionCallbackRecord(executor, callback))) {
-            Log.w(TAG, "registerSession2TokenCallback: Ignoring the same callback");
-            return;
-        }
-        synchronized (mLock) {
-            if (mCallbackStub == null) {
-                MediaCommunicationServiceCallbackStub callbackStub =
-                        new MediaCommunicationServiceCallbackStub();
-                try {
-                    getService().registerCallback(callbackStub, mContext.getPackageName());
-                    mCallbackStub = callbackStub;
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "Failed to register callback.", ex);
-                }
-            }
-        }
-    }
-
-    /**
-     * Stops receiving active sessions updates on the specified callback.
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    public void unregisterSessionCallback(@NonNull SessionCallback callback) {
-        if (!mTokenCallbackRecords.remove(
-                new SessionCallbackRecord(null, callback))) {
-            Log.w(TAG, "unregisterSession2TokenCallback: Ignoring an unknown callback.");
-            return;
-        }
-        synchronized (mLock) {
-            if (mCallbackStub != null && mTokenCallbackRecords.isEmpty()) {
-                try {
-                    getService().unregisterCallback(mCallbackStub);
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "Failed to unregister callback.", ex);
-                }
-                mCallbackStub = null;
-            }
-        }
-    }
-
-    private IMediaCommunicationService getService() {
-        if (mService == null) {
-            mService = IMediaCommunicationService.Stub.asInterface(
-                    MediaFrameworkInitializer.getMediaServiceManager()
-                            .getMediaCommunicationServiceRegisterer()
-                            .get());
-        }
-        return mService;
-    }
-
-    // TODO: remove this when MCS implements dispatchMediaKeyEvent.
-    private MediaSessionManager getMediaSessionManager() {
-        if (mMediaSessionManager == null) {
-            mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
-        }
-        return mMediaSessionManager;
-    }
-
-    private List<Session2Token> getSession2Tokens(int userId) {
-        try {
-            MediaParceledListSlice slice = getService().getSession2Tokens(userId);
-            return slice == null ? Collections.emptyList() : slice.getList();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get session tokens", e);
-        }
-        return Collections.emptyList();
-    }
-
-    /**
-     * Sends a media key event. The receiver will be selected automatically.
-     *
-     * @param keyEvent the key event to send
-     * @param asSystemService if {@code true}, the event sent to the session as if it was come from
-     *                        the system service instead of the app process.
-     * @hide
-     */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean asSystemService) {
-        Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null");
-
-        // When MCS handles this, caller is changed.
-        // TODO: remove this when MCS implementation is done.
-        if (!asSystemService) {
-            getMediaSessionManager().dispatchMediaKeyEvent(keyEvent, false);
-            return;
-        }
-
-        try {
-            getService().dispatchMediaKeyEvent(mContext.getPackageName(),
-                    keyEvent, asSystemService);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to send key event.", e);
-        }
-    }
-
-    /**
-     * Callback for listening to changes to the sessions.
-     * @see #registerSessionCallback(Executor, SessionCallback)
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    public interface SessionCallback {
-        /**
-         * Called when a new {@link MediaSession2 media session2} is created.
-         * @param token the newly created token
-         */
-        default void onSession2TokenCreated(@NonNull Session2Token token) {}
-
-        /**
-         * Called when {@link #getSession2Tokens() session tokens} are changed.
-         */
-        default void onSession2TokensChanged(@NonNull List<Session2Token> tokens) {}
-    }
-
-    private static final class SessionCallbackRecord {
-        public final Executor executor;
-        public final SessionCallback callback;
-
-        SessionCallbackRecord(Executor executor, SessionCallback callback) {
-            this.executor = executor;
-            this.callback = callback;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(callback);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (!(obj instanceof SessionCallbackRecord)) {
-                return false;
-            }
-            return Objects.equals(this.callback, ((SessionCallbackRecord) obj).callback);
-        }
-    }
-
-    class MediaCommunicationServiceCallbackStub extends IMediaCommunicationServiceCallback.Stub {
-        @Override
-        public void onSession2Created(Session2Token token) throws RemoteException {
-            for (SessionCallbackRecord record : mTokenCallbackRecords) {
-                record.executor.execute(() -> record.callback.onSession2TokenCreated(token));
-            }
-        }
-
-        @Override
-        public void onSession2Changed(MediaParceledListSlice tokens) throws RemoteException {
-            List<Session2Token> tokenList = tokens.getList();
-            for (SessionCallbackRecord record : mTokenCallbackRecords) {
-                record.executor.execute(() -> record.callback.onSession2TokensChanged(tokenList));
-            }
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaConstants.java b/apex/media/framework/java/android/media/MediaConstants.java
deleted file mode 100644
index ce10889..0000000
--- a/apex/media/framework/java/android/media/MediaConstants.java
+++ /dev/null
@@ -1,35 +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;
-
-class MediaConstants {
-    // Bundle key for int
-    static final String KEY_PID = "android.media.key.PID";
-
-    // Bundle key for String
-    static final String KEY_PACKAGE_NAME = "android.media.key.PACKAGE_NAME";
-
-    // Bundle key for Parcelable
-    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";
-    static final String KEY_TOKEN_EXTRAS = "android.media.key.TOKEN_EXTRAS";
-    static final String KEY_CONNECTION_HINTS = "android.media.key.CONNECTION_HINTS";
-
-    private MediaConstants() {
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaController2.java b/apex/media/framework/java/android/media/MediaController2.java
deleted file mode 100644
index 159e8e5..0000000
--- a/apex/media/framework/java/android/media/MediaController2.java
+++ /dev/null
@@ -1,637 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
-import static android.media.MediaConstants.KEY_CONNECTION_HINTS;
-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_SESSION2LINK;
-import static android.media.MediaConstants.KEY_TOKEN_EXTRAS;
-import static android.media.Session2Command.Result.RESULT_ERROR_UNKNOWN_ERROR;
-import static android.media.Session2Command.Result.RESULT_INFO_SKIPPED;
-import static android.media.Session2Token.TYPE_SESSION;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-import java.util.concurrent.Executor;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- *
- * Allows an app to interact with an active {@link MediaSession2} or a
- * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other
- * commands can be sent to the session.
- */
-public class MediaController2 implements AutoCloseable {
-    static final String TAG = "MediaController2";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final ControllerCallback mCallback;
-
-    private final IBinder.DeathRecipient mDeathRecipient = () -> close();
-    private final Context mContext;
-    private final Session2Token mSessionToken;
-    private final Executor mCallbackExecutor;
-    private final Controller2Link mControllerStub;
-    private final Handler mResultHandler;
-    private final SessionServiceConnection mServiceConnection;
-
-    private final Object mLock = new Object();
-    //@GuardedBy("mLock")
-    private boolean mClosed;
-    //@GuardedBy("mLock")
-    private int mNextSeqNumber;
-    //@GuardedBy("mLock")
-    private Session2Link mSessionBinder;
-    //@GuardedBy("mLock")
-    private Session2CommandGroup mAllowedCommands;
-    //@GuardedBy("mLock")
-    private Session2Token mConnectedToken;
-    //@GuardedBy("mLock")
-    private ArrayMap<ResultReceiver, Integer> mPendingCommands;
-    //@GuardedBy("mLock")
-    private ArraySet<Integer> mRequestedCommandSeqNumbers;
-    //@GuardedBy("mLock")
-    private boolean mPlaybackActive;
-
-    /**
-     * Create a {@link MediaController2} from the {@link Session2Token}.
-     * This connects to the session and may wake up the service if it's not available.
-     *
-     * @param context context
-     * @param token token to connect to
-     * @param connectionHints a session-specific argument to send to the session when connecting.
-     *                        The contents of this bundle may affect the connection result.
-     * @param executor executor to run callbacks on.
-     * @param callback controller callback to receive changes in.
-     */
-    MediaController2(@NonNull Context context, @NonNull Session2Token token,
-            @NonNull Bundle connectionHints, @NonNull Executor executor,
-            @NonNull ControllerCallback callback) {
-        if (context == null) {
-            throw new IllegalArgumentException("context shouldn't be null");
-        }
-        if (token == null) {
-            throw new IllegalArgumentException("token shouldn't be null");
-        }
-        mContext = context;
-        mSessionToken = token;
-        mCallbackExecutor = (executor == null) ? context.getMainExecutor() : executor;
-        mCallback = (callback == null) ? new ControllerCallback() {} : callback;
-        mControllerStub = new Controller2Link(this);
-        // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
-        mResultHandler = new Handler(context.getMainLooper());
-
-        mNextSeqNumber = 0;
-        mPendingCommands = new ArrayMap<>();
-        mRequestedCommandSeqNumbers = new ArraySet<>();
-
-        boolean connectRequested;
-        if (token.getType() == TYPE_SESSION) {
-            mServiceConnection = null;
-            connectRequested = requestConnectToSession(connectionHints);
-        } else {
-            mServiceConnection = new SessionServiceConnection(connectionHints);
-            connectRequested = requestConnectToService();
-        }
-        if (!connectRequested) {
-            close();
-        }
-    }
-
-    @Override
-    public void close() {
-        synchronized (mLock) {
-            if (mClosed) {
-                // Already closed. Ignore rest of clean up code.
-                // Note: unbindService() throws IllegalArgumentException when it's called twice.
-                return;
-            }
-            if (DEBUG) {
-                Log.d(TAG, "closing " + this);
-            }
-            mClosed = true;
-            if (mServiceConnection != null) {
-                // Note: This should be called even when the bindService() has returned false.
-                mContext.unbindService(mServiceConnection);
-            }
-            if (mSessionBinder != null) {
-                try {
-                    mSessionBinder.disconnect(mControllerStub, getNextSeqNumber());
-                    mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
-                } catch (RuntimeException e) {
-                    // No-op
-                }
-            }
-            mConnectedToken = null;
-            mPendingCommands.clear();
-            mRequestedCommandSeqNumbers.clear();
-            mCallbackExecutor.execute(() -> {
-                mCallback.onDisconnected(MediaController2.this);
-            });
-            mSessionBinder = null;
-        }
-    }
-
-    /**
-     * Returns {@link Session2Token} of the connected session.
-     * If it is not connected yet, it returns {@code null}.
-     * <p>
-     * This may differ with the {@link Session2Token} from the constructor. For example, if the
-     * controller is created with the token for {@link MediaSession2Service}, this would return
-     * token for the {@link MediaSession2} in the service.
-     *
-     * @return Session2Token of the connected session, or {@code null} if not connected
-     */
-    @Nullable
-    public Session2Token getConnectedToken() {
-        synchronized (mLock) {
-            return mConnectedToken;
-        }
-    }
-
-    /**
-     * Returns whether the session's playback is active.
-     *
-     * @return {@code true} if playback active. {@code false} otherwise.
-     * @see ControllerCallback#onPlaybackActiveChanged(MediaController2, boolean)
-     */
-    public boolean isPlaybackActive() {
-        synchronized (mLock) {
-            return mPlaybackActive;
-        }
-    }
-
-    /**
-     * Sends a session command to the session
-     * <p>
-     * @param command the session command
-     * @param args optional arguments
-     * @return a token which will be sent together in {@link ControllerCallback#onCommandResult}
-     *        when its result is received.
-     */
-    @NonNull
-    public Object sendSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
-        if (command == null) {
-            throw new IllegalArgumentException("command shouldn't be null");
-        }
-
-        ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) {
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                synchronized (mLock) {
-                    mPendingCommands.remove(this);
-                }
-                mCallbackExecutor.execute(() -> {
-                    mCallback.onCommandResult(MediaController2.this, this,
-                            command, new Session2Command.Result(resultCode, resultData));
-                });
-            }
-        };
-
-        synchronized (mLock) {
-            if (mSessionBinder != null) {
-                int seq = getNextSeqNumber();
-                mPendingCommands.put(resultReceiver, seq);
-                try {
-                    mSessionBinder.sendSessionCommand(mControllerStub, seq, command, args,
-                            resultReceiver);
-                } catch (RuntimeException e)  {
-                    mPendingCommands.remove(resultReceiver);
-                    resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null);
-                }
-            }
-        }
-        return resultReceiver;
-    }
-
-    /**
-     * Cancels the session command previously sent.
-     *
-     * @param token the token which is returned from {@link #sendSessionCommand}.
-     */
-    public void cancelSessionCommand(@NonNull Object token) {
-        if (token == null) {
-            throw new IllegalArgumentException("token shouldn't be null");
-        }
-        synchronized (mLock) {
-            if (mSessionBinder == null) return;
-            Integer seq = mPendingCommands.remove(token);
-            if (seq != null) {
-                mSessionBinder.cancelSessionCommand(mControllerStub, seq);
-            }
-        }
-    }
-
-    // Called by Controller2Link.onConnected
-    void onConnected(int seq, Bundle connectionResult) {
-        Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK);
-        Session2CommandGroup allowedCommands =
-                connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
-        boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE);
-
-        Bundle tokenExtras = connectionResult.getBundle(KEY_TOKEN_EXTRAS);
-        if (tokenExtras == null) {
-            Log.w(TAG, "extras shouldn't be null.");
-            tokenExtras = Bundle.EMPTY;
-        } else if (MediaSession2.hasCustomParcelable(tokenExtras)) {
-            Log.w(TAG, "extras contain custom parcelable. Ignoring.");
-            tokenExtras = Bundle.EMPTY;
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder
-                    + ", allowedCommands=" + allowedCommands);
-        }
-        if (sessionBinder == null || allowedCommands == null) {
-            // Connection rejected.
-            close();
-            return;
-        }
-        synchronized (mLock) {
-            mSessionBinder = sessionBinder;
-            mAllowedCommands = allowedCommands;
-            mPlaybackActive = playbackActive;
-
-            // Implementation for the local binder is no-op,
-            // so can be used without worrying about deadlock.
-            sessionBinder.linkToDeath(mDeathRecipient, 0);
-            mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION,
-                    mSessionToken.getPackageName(), sessionBinder, tokenExtras);
-        }
-        mCallbackExecutor.execute(() -> {
-            mCallback.onConnected(MediaController2.this, allowedCommands);
-        });
-    }
-
-    // Called by Controller2Link.onDisconnected
-    void onDisconnected(int seq) {
-        // close() will call mCallback.onDisconnected
-        close();
-    }
-
-    // Called by Controller2Link.onPlaybackActiveChanged
-    void onPlaybackActiveChanged(int seq, boolean playbackActive) {
-        synchronized (mLock) {
-            mPlaybackActive = playbackActive;
-        }
-        mCallbackExecutor.execute(() -> {
-            mCallback.onPlaybackActiveChanged(MediaController2.this, playbackActive);
-        });
-    }
-
-    // Called by Controller2Link.onSessionCommand
-    void onSessionCommand(int seq, Session2Command command, Bundle args,
-            @Nullable ResultReceiver resultReceiver) {
-        synchronized (mLock) {
-            mRequestedCommandSeqNumbers.add(seq);
-        }
-        mCallbackExecutor.execute(() -> {
-            boolean isCanceled;
-            synchronized (mLock) {
-                isCanceled = !mRequestedCommandSeqNumbers.remove(seq);
-            }
-            if (isCanceled) {
-                if (resultReceiver != null) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                }
-                return;
-            }
-            Session2Command.Result result = mCallback.onSessionCommand(
-                    MediaController2.this, command, args);
-            if (resultReceiver != null) {
-                if (result == null) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                } else {
-                    resultReceiver.send(result.getResultCode(), result.getResultData());
-                }
-            }
-        });
-    }
-
-    // Called by Controller2Link.onSessionCommand
-    void onCancelCommand(int seq) {
-        synchronized (mLock) {
-            mRequestedCommandSeqNumbers.remove(seq);
-        }
-    }
-
-    private int getNextSeqNumber() {
-        synchronized (mLock) {
-            return mNextSeqNumber++;
-        }
-    }
-
-    private Bundle createConnectionRequest(@NonNull Bundle connectionHints) {
-        Bundle connectionRequest = new Bundle();
-        connectionRequest.putString(KEY_PACKAGE_NAME, mContext.getPackageName());
-        connectionRequest.putInt(KEY_PID, Process.myPid());
-        connectionRequest.putBundle(KEY_CONNECTION_HINTS, connectionHints);
-        return connectionRequest;
-    }
-
-    private boolean requestConnectToSession(@NonNull Bundle connectionHints) {
-        Session2Link sessionBinder = mSessionToken.getSessionLink();
-        Bundle connectionRequest = createConnectionRequest(connectionHints);
-        try {
-            sessionBinder.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
-        } catch (RuntimeException e) {
-            Log.w(TAG, "Failed to call connection request", e);
-            return false;
-        }
-        return true;
-    }
-
-    private boolean requestConnectToService() {
-        // Service. Needs to get fresh binder whenever connection is needed.
-        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
-        // reasons.
-        // 1. Prevent session service owner's stopSelf() from destroying service.
-        //    With the startForegroundService(), service's call of stopSelf() will trigger immediate
-        //    onDestroy() calls on the main thread even when onConnect() is running in another
-        //    thread.
-        // 2. Minimize APIs for developers to take care about.
-        //    With bindService(), developers only need to take care about Service.onBind()
-        //    but Service.onStartCommand() should be also taken care about with the
-        //    startForegroundService().
-        // 3. Future support for UI-less playback
-        //    If a service wants to keep running, it should be either foreground service or
-        //    bound service. But there had been request for the feature for system apps
-        //    and using bindService() will be better fit with it.
-        synchronized (mLock) {
-            boolean result = mContext.bindService(
-                    intent, mServiceConnection, Context.BIND_AUTO_CREATE);
-            if (!result) {
-                Log.w(TAG, "bind to " + mSessionToken + " failed");
-                return false;
-            } else if (DEBUG) {
-                Log.d(TAG, "bind to " + mSessionToken + " succeeded");
-            }
-        }
-        return true;
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Builder for {@link MediaController2}.
-     * <p>
-     * Any incoming event from the {@link MediaSession2} will be handled on the callback
-     * executor. If it's not set, {@link Context#getMainExecutor()} will be used by default.
-     */
-    public static final class Builder {
-        private Context mContext;
-        private Session2Token mToken;
-        private Bundle mConnectionHints;
-        private Executor mCallbackExecutor;
-        private ControllerCallback mCallback;
-
-        /**
-         * Creates a builder for {@link MediaController2}.
-         *
-         * @param context context
-         * @param token token of the session to connect to
-         */
-        public Builder(@NonNull Context context, @NonNull Session2Token token) {
-            if (context == null) {
-                throw new IllegalArgumentException("context shouldn't be null");
-            }
-            if (token == null) {
-                throw new IllegalArgumentException("token shouldn't be null");
-            }
-            mContext = context;
-            mToken = token;
-        }
-
-        /**
-         * Set the connection hints for the controller.
-         * <p>
-         * {@code connectionHints} is a session-specific argument to send to the session when
-         * connecting. The contents of this bundle may affect the connection result.
-         * <p>
-         * An {@link IllegalArgumentException} will be thrown if the bundle contains any
-         * non-framework Parcelable objects.
-         *
-         * @param connectionHints a bundle which contains the connection hints
-         * @return The Builder to allow chaining
-         */
-        @NonNull
-        public Builder setConnectionHints(@NonNull Bundle connectionHints) {
-            if (connectionHints == null) {
-                throw new IllegalArgumentException("connectionHints shouldn't be null");
-            }
-            if (MediaSession2.hasCustomParcelable(connectionHints)) {
-                throw new IllegalArgumentException("connectionHints shouldn't contain any custom "
-                        + "parcelables");
-            }
-            mConnectionHints = new Bundle(connectionHints);
-            return this;
-        }
-
-        /**
-         * Set callback for the controller and its executor.
-         *
-         * @param executor callback executor
-         * @param callback session callback.
-         * @return The Builder to allow chaining
-         */
-        @NonNull
-        public Builder setControllerCallback(@NonNull Executor executor,
-                @NonNull ControllerCallback callback) {
-            if (executor == null) {
-                throw new IllegalArgumentException("executor shouldn't be null");
-            }
-            if (callback == null) {
-                throw new IllegalArgumentException("callback shouldn't be null");
-            }
-            mCallbackExecutor = executor;
-            mCallback = callback;
-            return this;
-        }
-
-        /**
-         * Build {@link MediaController2}.
-         *
-         * @return a new controller
-         */
-        @NonNull
-        public MediaController2 build() {
-            if (mCallbackExecutor == null) {
-                mCallbackExecutor = mContext.getMainExecutor();
-            }
-            if (mCallback == null) {
-                mCallback = new ControllerCallback() {};
-            }
-            if (mConnectionHints == null) {
-                mConnectionHints = Bundle.EMPTY;
-            }
-            return new MediaController2(
-                    mContext, mToken, mConnectionHints, mCallbackExecutor, mCallback);
-        }
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Interface for listening to change in activeness of the {@link MediaSession2}.
-     */
-    public abstract static class ControllerCallback {
-        /**
-         * Called when the controller is successfully connected to the session. The controller
-         * becomes available afterwards.
-         *
-         * @param controller the controller for this event
-         * @param allowedCommands commands that's allowed by the session.
-         */
-        public void onConnected(@NonNull MediaController2 controller,
-                @NonNull Session2CommandGroup allowedCommands) {}
-
-        /**
-         * Called when the session refuses the controller or the controller is disconnected from
-         * the session. The controller becomes unavailable afterwards and the callback wouldn't
-         * be called.
-         * <p>
-         * It will be also called after the {@link #close()}, so you can put clean up code here.
-         * You don't need to call {@link #close()} after this.
-         *
-         * @param controller the controller for this event
-         */
-        public void onDisconnected(@NonNull MediaController2 controller) {}
-
-        /**
-         * Called when the session's playback activeness is changed.
-         *
-         * @param controller the controller for this event
-         * @param playbackActive {@code true} if the session's playback is active.
-         *                       {@code false} otherwise.
-         * @see MediaController2#isPlaybackActive()
-         */
-        public void onPlaybackActiveChanged(@NonNull MediaController2 controller,
-                boolean playbackActive) {}
-
-        /**
-         * Called when the connected session sent a session command.
-         *
-         * @param controller the controller for this event
-         * @param command the session command
-         * @param args optional arguments
-         * @return the result for the session command. If {@code null}, RESULT_INFO_SKIPPED
-         *         will be sent to the session.
-         */
-        @Nullable
-        public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller,
-                @NonNull Session2Command command, @Nullable Bundle args) {
-            return null;
-        }
-
-        /**
-         * Called when the command sent to the connected session is finished.
-         *
-         * @param controller the controller for this event
-         * @param token the token got from {@link MediaController2#sendSessionCommand}
-         * @param command the session command
-         * @param result the result of the session command
-         */
-        public void onCommandResult(@NonNull MediaController2 controller, @NonNull Object token,
-                @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
-    }
-
-    // This will be called on the main thread.
-    private class SessionServiceConnection implements ServiceConnection {
-        private final Bundle mConnectionHints;
-
-        SessionServiceConnection(@Nullable Bundle connectionHints) {
-            mConnectionHints = connectionHints;
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            // Note that it's always main-thread.
-            boolean connectRequested = false;
-            try {
-                if (DEBUG) {
-                    Log.d(TAG, "onServiceConnected " + name + " " + this);
-                }
-                if (!mSessionToken.getPackageName().equals(name.getPackageName())) {
-                    Log.wtf(TAG, "Expected connection to " + mSessionToken.getPackageName()
-                            + " but is connected to " + name);
-                    return;
-                }
-                IMediaSession2Service iService = IMediaSession2Service.Stub.asInterface(service);
-                if (iService == null) {
-                    Log.wtf(TAG, "Service interface is missing.");
-                    return;
-                }
-                Bundle connectionRequest = createConnectionRequest(mConnectionHints);
-                iService.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
-                connectRequested = true;
-            } catch (RemoteException e) {
-                Log.w(TAG, "Service " + name + " has died prematurely", e);
-            } finally {
-                if (!connectRequested) {
-                    close();
-                }
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            // Temporal lose of the binding because of the service crash. System will automatically
-            // rebind, so just no-op.
-            if (DEBUG) {
-                Log.w(TAG, "Session service " + name + " is disconnected.");
-            }
-            close();
-        }
-
-        @Override
-        public void onBindingDied(ComponentName name) {
-            // Permanent lose of the binding because of the service package update or removed.
-            // This SessionServiceRecord will be removed accordingly, but forget session binder here
-            // for sure.
-            close();
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaFeature.java b/apex/media/framework/java/android/media/MediaFeature.java
deleted file mode 100644
index 8d1b159..0000000
--- a/apex/media/framework/java/android/media/MediaFeature.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.StringDef;
-import android.os.Build;
-
-import com.android.modules.annotation.MinSdk;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * MediaFeature defines various media features, e.g. hdr type.
- */
-@MinSdk(Build.VERSION_CODES.S)
-public final class MediaFeature {
-     /**
-     * Defines tye type of HDR(high dynamic range) video.
-     */
-    public static final class HdrType {
-        private HdrType() {
-        }
-
-        /**
-         * HDR type for dolby-vision.
-         */
-        public static final String DOLBY_VISION = "android.media.feature.hdr.dolby_vision";
-        /**
-         * HDR type for hdr10.
-         */
-        public static final String HDR10 = "android.media.feature.hdr.hdr10";
-        /**
-         * HDR type for hdr10+.
-         */
-        public static final String HDR10_PLUS = "android.media.feature.hdr.hdr10_plus";
-        /**
-         * HDR type for hlg.
-         */
-        public static final String HLG = "android.media.feature.hdr.hlg";
-    }
-
-    /** @hide */
-    @StringDef({
-            HdrType.DOLBY_VISION,
-            HdrType.HDR10,
-            HdrType.HDR10_PLUS,
-            HdrType.HLG,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface MediaHdrType {
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
deleted file mode 100644
index d7ad97d..0000000
--- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.annotation.SystemApi.Client;
-import android.app.SystemServiceRegistry;
-import android.content.Context;
-import android.os.Build;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.modules.annotation.MinSdk;
-import com.android.modules.utils.build.SdkLevel;
-
-/**
- * Class for performing registration for all media services on com.android.media apex.
- *
- * @hide
- */
-@MinSdk(Build.VERSION_CODES.S)
-@RequiresApi(Build.VERSION_CODES.S)
-@SystemApi(client = Client.MODULE_LIBRARIES)
-public class MediaFrameworkInitializer {
-    private MediaFrameworkInitializer() {
-    }
-
-    private static volatile MediaServiceManager sMediaServiceManager;
-
-    /**
-     * Sets an instance of {@link MediaServiceManager} that allows
-     * the media mainline module to register/obtain media binder services. This is called
-     * by the platform during the system initialization.
-     *
-     * @param mediaServiceManager instance of {@link MediaServiceManager} that allows
-     * the media mainline module to register/obtain media binder services.
-     */
-    public static void setMediaServiceManager(
-            @NonNull MediaServiceManager mediaServiceManager) {
-        if (sMediaServiceManager != null) {
-            throw new IllegalStateException("setMediaServiceManager called twice!");
-        }
-
-        if (mediaServiceManager == null) {
-            throw new NullPointerException("mediaServiceManager is null!");
-        }
-
-        sMediaServiceManager = mediaServiceManager;
-    }
-
-    /** @hide */
-    public static MediaServiceManager getMediaServiceManager() {
-        return sMediaServiceManager;
-    }
-
-    /**
-     * Called by {@link SystemServiceRegistry}'s static initializer and registers all media
-     * services to {@link Context}, so that {@link Context#getSystemService} can return them.
-     *
-     * @throws IllegalStateException if this is called from anywhere besides
-     * {@link SystemServiceRegistry}
-     */
-    public static void registerServiceWrappers() {
-        SystemServiceRegistry.registerContextAwareService(
-                Context.MEDIA_TRANSCODING_SERVICE,
-                MediaTranscodingManager.class,
-                context -> new MediaTranscodingManager(context)
-        );
-        if (SdkLevel.isAtLeastS()) {
-            SystemServiceRegistry.registerContextAwareService(
-                    Context.MEDIA_COMMUNICATION_SERVICE,
-                    MediaCommunicationManager.class,
-                    context -> new MediaCommunicationManager(context)
-            );
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaParceledListSlice.java b/apex/media/framework/java/android/media/MediaParceledListSlice.java
deleted file mode 100644
index fb36e80..0000000
--- a/apex/media/framework/java/android/media/MediaParceledListSlice.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import androidx.annotation.RequiresApi;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This is a copied version of MediaParceledListSlice in framework with hidden API usages removed,
- * and also with some lint error fixed.
- *
- * Transfer a large list of Parcelable objects across an IPC.  Splits into
- * multiple transactions if needed.
- *
- * TODO: Remove this from @SystemApi once all the MediaSession related classes are moved
- *       to apex (or ParceledListSlice moved to apex). This class is temporaily added to system API
- *       for moving classes step by step.
- *
- * @param <T> The type of the elements in the list.
- * @see BaseMediaParceledListSlice
- * @deprecated This is temporary marked as @SystemApi. Should be removed from the API surface.
- * @hide
- */
-@Deprecated
-@RequiresApi(Build.VERSION_CODES.S)
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-public final class MediaParceledListSlice<T extends Parcelable>
-        extends BaseMediaParceledListSlice<T> {
-    public MediaParceledListSlice(@NonNull List<T> list) {
-        super(list);
-    }
-
-    private MediaParceledListSlice(Parcel in, ClassLoader loader) {
-        super(in, loader);
-    }
-
-    @NonNull
-    public static <T extends Parcelable> MediaParceledListSlice<T> emptyList() {
-        return new MediaParceledListSlice<T>(Collections.<T> emptyList());
-    }
-
-    @Override
-    public int describeContents() {
-        int contents = 0;
-        final List<T> list = getList();
-        for (int i=0; i<list.size(); i++) {
-            contents |= list.get(i).describeContents();
-        }
-        return contents;
-    }
-
-    @Override
-    void writeElement(T parcelable, Parcel dest, int callFlags) {
-        parcelable.writeToParcel(dest, callFlags);
-    }
-
-    @Override
-    void writeParcelableCreator(T parcelable, Parcel dest) {
-        dest.writeParcelableCreator((Parcelable) parcelable);
-    }
-
-    @Override
-    Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) {
-        return from.readParcelableCreator(loader);
-    }
-
-    @NonNull
-    @SuppressWarnings("unchecked")
-    public static final Parcelable.ClassLoaderCreator<MediaParceledListSlice> CREATOR =
-            new Parcelable.ClassLoaderCreator<MediaParceledListSlice>() {
-        public MediaParceledListSlice createFromParcel(Parcel in) {
-            return new MediaParceledListSlice(in, null);
-        }
-
-        @Override
-        public MediaParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {
-            return new MediaParceledListSlice(in, loader);
-        }
-
-        @Override
-        public MediaParceledListSlice[] newArray(int size) {
-            return new MediaParceledListSlice[size];
-        }
-    };
-}
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
deleted file mode 100644
index b6f85c7..0000000
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ /dev/null
@@ -1,2293 +0,0 @@
-/*
- * 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.media;
-
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-import android.media.MediaCodec.CryptoInfo;
-import android.media.metrics.LogSessionId;
-import android.os.Build;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.modules.utils.build.SdkLevel;
-
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.Format;
-import com.google.android.exoplayer2.ParserException;
-import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
-import com.google.android.exoplayer2.extractor.ChunkIndex;
-import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
-import com.google.android.exoplayer2.extractor.Extractor;
-import com.google.android.exoplayer2.extractor.ExtractorInput;
-import com.google.android.exoplayer2.extractor.ExtractorOutput;
-import com.google.android.exoplayer2.extractor.PositionHolder;
-import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints;
-import com.google.android.exoplayer2.extractor.TrackOutput;
-import com.google.android.exoplayer2.extractor.amr.AmrExtractor;
-import com.google.android.exoplayer2.extractor.flac.FlacExtractor;
-import com.google.android.exoplayer2.extractor.flv.FlvExtractor;
-import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
-import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor;
-import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
-import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor;
-import com.google.android.exoplayer2.extractor.ogg.OggExtractor;
-import com.google.android.exoplayer2.extractor.ts.Ac3Extractor;
-import com.google.android.exoplayer2.extractor.ts.Ac4Extractor;
-import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
-import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory;
-import com.google.android.exoplayer2.extractor.ts.PsExtractor;
-import com.google.android.exoplayer2.extractor.ts.TsExtractor;
-import com.google.android.exoplayer2.extractor.wav.WavExtractor;
-import com.google.android.exoplayer2.upstream.DataReader;
-import com.google.android.exoplayer2.util.ParsableByteArray;
-import com.google.android.exoplayer2.util.TimestampAdjuster;
-import com.google.android.exoplayer2.util.Util;
-import com.google.android.exoplayer2.video.ColorInfo;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.function.Function;
-
-/**
- * Parses media container formats and extracts contained media samples and metadata.
- *
- * <p>This class provides access to a battery of low-level media container parsers. Each instance of
- * this class is associated to a specific media parser implementation which is suitable for
- * extraction from a specific media container format. The media parser implementation assignment
- * depends on the factory method (see {@link #create} and {@link #createByName}) used to create the
- * instance.
- *
- * <p>Users must implement the following to use this class.
- *
- * <ul>
- *   <li>{@link InputReader}: Provides the media container's bytes to parse.
- *   <li>{@link OutputConsumer}: Provides a sink for all extracted data and metadata.
- * </ul>
- *
- * <p>The following code snippet includes a usage example:
- *
- * <pre>
- * MyOutputConsumer myOutputConsumer = new MyOutputConsumer();
- * MyInputReader myInputReader = new MyInputReader("www.example.com");
- * MediaParser mediaParser = MediaParser.create(myOutputConsumer);
- *
- * while (mediaParser.advance(myInputReader)) {}
- *
- * mediaParser.release();
- * mediaParser = null;
- * </pre>
- *
- * <p>The following code snippet provides a rudimentary {@link OutputConsumer} sample implementation
- * which extracts and publishes all video samples:
- *
- * <pre>
- * class VideoOutputConsumer implements MediaParser.OutputConsumer {
- *
- *     private byte[] sampleDataBuffer = new byte[4096];
- *     private byte[] discardedDataBuffer = new byte[4096];
- *     private int videoTrackIndex = -1;
- *     private int bytesWrittenCount = 0;
- *
- *     &#64;Override
- *     public void onSeekMapFound(int i, &#64;NonNull MediaFormat mediaFormat) {
- *       // Do nothing.
- *     }
- *
- *     &#64;Override
- *     public void onTrackDataFound(int i, &#64;NonNull TrackData trackData) {
- *       MediaFormat mediaFormat = trackData.mediaFormat;
- *       if (videoTrackIndex == -1 &amp;&amp;
- *           mediaFormat
- *               .getString(MediaFormat.KEY_MIME, &#47;* defaultValue= *&#47; "")
- *               .startsWith("video/")) {
- *         videoTrackIndex = i;
- *       }
- *     }
- *
- *     &#64;Override
- *     public void onSampleDataFound(int trackIndex, &#64;NonNull InputReader inputReader)
- *         throws IOException {
- *       int numberOfBytesToRead = (int) inputReader.getLength();
- *       if (videoTrackIndex != trackIndex) {
- *         // Discard contents.
- *         inputReader.read(
- *             discardedDataBuffer,
- *             &#47;* offset= *&#47; 0,
- *             Math.min(discardDataBuffer.length, numberOfBytesToRead));
- *       } else {
- *         ensureSpaceInBuffer(numberOfBytesToRead);
- *         int bytesRead = inputReader.read(
- *             sampleDataBuffer, bytesWrittenCount, numberOfBytesToRead);
- *         bytesWrittenCount += bytesRead;
- *       }
- *     }
- *
- *     &#64;Override
- *     public void onSampleCompleted(
- *         int trackIndex,
- *         long timeMicros,
- *         int flags,
- *         int size,
- *         int offset,
- *         &#64;Nullable CryptoInfo cryptoData) {
- *       if (videoTrackIndex != trackIndex) {
- *         return; // It's not the video track. Ignore.
- *       }
- *       byte[] sampleData = new byte[size];
- *       int sampleStartOffset = bytesWrittenCount - size - offset;
- *       System.arraycopy(
- *           sampleDataBuffer,
- *           sampleStartOffset,
- *           sampleData,
- *           &#47;* destPos= *&#47; 0,
- *           size);
- *       // Place trailing bytes at the start of the buffer.
- *       System.arraycopy(
- *           sampleDataBuffer,
- *           bytesWrittenCount - offset,
- *           sampleDataBuffer,
- *           &#47;* destPos= *&#47; 0,
- *           &#47;* size= *&#47; offset);
- *       bytesWrittenCount = bytesWrittenCount - offset;
- *       publishSample(sampleData, timeMicros, flags);
- *     }
- *
- *    private void ensureSpaceInBuffer(int numberOfBytesToRead) {
- *      int requiredLength = bytesWrittenCount + numberOfBytesToRead;
- *      if (requiredLength &gt; sampleDataBuffer.length) {
- *        sampleDataBuffer = Arrays.copyOf(sampleDataBuffer, requiredLength);
- *      }
- *    }
- *
- *   }
- *
- * </pre>
- */
-@RequiresApi(Build.VERSION_CODES.R)
-public final class MediaParser {
-
-    /**
-     * Maps seek positions to {@link SeekPoint SeekPoints} in the stream.
-     *
-     * <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start
-     * playing media samples.
-     */
-    public static final class SeekMap {
-
-        /** Returned by {@link #getDurationMicros()} when the duration is unknown. */
-        public static final int UNKNOWN_DURATION = Integer.MIN_VALUE;
-
-        /**
-         * For each {@link #getSeekPoints} call, returns a single {@link SeekPoint} whose {@link
-         * SeekPoint#timeMicros} matches the requested timestamp, and whose {@link
-         * SeekPoint#position} is 0.
-         *
-         * @hide
-         */
-        public static final SeekMap DUMMY = new SeekMap(new DummyExoPlayerSeekMap());
-
-        private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
-
-        private SeekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
-            mExoPlayerSeekMap = exoplayerSeekMap;
-        }
-
-        /** Returns whether seeking is supported. */
-        public boolean isSeekable() {
-            return mExoPlayerSeekMap.isSeekable();
-        }
-
-        /**
-         * Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the
-         * duration is unknown.
-         */
-        public long getDurationMicros() {
-            long durationUs = mExoPlayerSeekMap.getDurationUs();
-            return durationUs != C.TIME_UNSET ? durationUs : UNKNOWN_DURATION;
-        }
-
-        /**
-         * Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds.
-         *
-         * <p>{@code getSeekPoints(timeMicros).first} contains the latest seek point for samples
-         * with timestamp equal to or smaller than {@code timeMicros}.
-         *
-         * <p>{@code getSeekPoints(timeMicros).second} contains the earliest seek point for samples
-         * with timestamp equal to or greater than {@code timeMicros}. If a seek point exists for
-         * {@code timeMicros}, the returned pair will contain the same {@link SeekPoint} twice.
-         *
-         * @param timeMicros A seek time in microseconds.
-         * @return The corresponding {@link SeekPoint SeekPoints}.
-         */
-        @NonNull
-        public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeMicros) {
-            SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeMicros);
-            return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
-        }
-    }
-
-    /** Holds information associated with a track. */
-    public static final class TrackData {
-
-        /** Holds {@link MediaFormat} information for the track. */
-        @NonNull public final MediaFormat mediaFormat;
-
-        /**
-         * Holds {@link DrmInitData} necessary to acquire keys associated with the track, or null if
-         * the track has no encryption data.
-         */
-        @Nullable public final DrmInitData drmInitData;
-
-        private TrackData(MediaFormat mediaFormat, DrmInitData drmInitData) {
-            this.mediaFormat = mediaFormat;
-            this.drmInitData = drmInitData;
-        }
-    }
-
-    /** Defines a seek point in a media stream. */
-    public static final class SeekPoint {
-
-        /** A {@link SeekPoint} whose time and byte offset are both set to 0. */
-        @NonNull public static final SeekPoint START = new SeekPoint(0, 0);
-
-        /** The time of the seek point, in microseconds. */
-        public final long timeMicros;
-
-        /** The byte offset of the seek point. */
-        public final long position;
-
-        /**
-         * @param timeMicros The time of the seek point, in microseconds.
-         * @param position The byte offset of the seek point.
-         */
-        private SeekPoint(long timeMicros, long position) {
-            this.timeMicros = timeMicros;
-            this.position = position;
-        }
-
-        @Override
-        @NonNull
-        public String toString() {
-            return "[timeMicros=" + timeMicros + ", position=" + position + "]";
-        }
-
-        @Override
-        public boolean equals(@Nullable Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null || getClass() != obj.getClass()) {
-                return false;
-            }
-            SeekPoint other = (SeekPoint) obj;
-            return timeMicros == other.timeMicros && position == other.position;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = (int) timeMicros;
-            result = 31 * result + (int) position;
-            return result;
-        }
-    }
-
-    /** Provides input data to {@link MediaParser}. */
-    public interface InputReader {
-
-        /**
-         * Reads up to {@code readLength} bytes of data and stores them into {@code buffer},
-         * starting at index {@code offset}.
-         *
-         * <p>This method blocks until at least one byte is read, the end of input is detected, or
-         * an exception is thrown. The read position advances to the first unread byte.
-         *
-         * @param buffer The buffer into which the read data should be stored.
-         * @param offset The start offset into {@code buffer} at which data should be written.
-         * @param readLength The maximum number of bytes to read.
-         * @return The non-zero number of bytes read, or -1 if no data is available because the end
-         *     of the input has been reached.
-         * @throws java.io.IOException If an error occurs reading from the source.
-         */
-        int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException;
-
-        /** Returns the current read position (byte offset) in the stream. */
-        long getPosition();
-
-        /** Returns the length of the input in bytes, or -1 if the length is unknown. */
-        long getLength();
-    }
-
-    /** {@link InputReader} that allows setting the read position. */
-    public interface SeekableInputReader extends InputReader {
-
-        /**
-         * Sets the read position at the given {@code position}.
-         *
-         * <p>{@link #advance} will immediately return after calling this method.
-         *
-         * @param position The position to seek to, in bytes.
-         */
-        void seekToPosition(long position);
-    }
-
-    /** Receives extracted media sample data and metadata from {@link MediaParser}. */
-    public interface OutputConsumer {
-
-        /**
-         * Called when a {@link SeekMap} has been extracted from the stream.
-         *
-         * <p>This method is called at least once before any samples are {@link #onSampleCompleted
-         * complete}. May be called multiple times after that in order to add {@link SeekPoint
-         * SeekPoints}.
-         *
-         * @param seekMap The extracted {@link SeekMap}.
-         */
-        void onSeekMapFound(@NonNull SeekMap seekMap);
-
-        /**
-         * Called when the number of tracks is found.
-         *
-         * @param numberOfTracks The number of tracks in the stream.
-         */
-        void onTrackCountFound(int numberOfTracks);
-
-        /**
-         * Called when new {@link TrackData} is found in the stream.
-         *
-         * @param trackIndex The index of the track for which the {@link TrackData} was extracted.
-         * @param trackData The extracted {@link TrackData}.
-         */
-        void onTrackDataFound(int trackIndex, @NonNull TrackData trackData);
-
-        /**
-         * Called when sample data is found in the stream.
-         *
-         * <p>If the invocation of this method returns before the entire {@code inputReader} {@link
-         * InputReader#getLength() length} is consumed, the method will be called again for the
-         * implementer to read the remaining data. Implementers should surface any thrown {@link
-         * IOException} caused by reading from {@code input}.
-         *
-         * @param trackIndex The index of the track to which the sample data corresponds.
-         * @param inputReader The {@link InputReader} from which to read the data.
-         * @throws IOException If an exception occurs while reading from {@code inputReader}.
-         */
-        void onSampleDataFound(int trackIndex, @NonNull InputReader inputReader) throws IOException;
-
-        /**
-         * Called once all the data of a sample has been passed to {@link #onSampleDataFound}.
-         *
-         * <p>Includes sample metadata, like presentation timestamp and flags.
-         *
-         * @param trackIndex The index of the track to which the sample corresponds.
-         * @param timeMicros The media timestamp associated with the sample, in microseconds.
-         * @param flags Flags associated with the sample. See the {@code SAMPLE_FLAG_*} constants.
-         * @param size The size of the sample data, in bytes.
-         * @param offset The number of bytes that have been consumed by {@code
-         *     onSampleDataFound(int, MediaParser.InputReader)} for the specified track, since the
-         *     last byte belonging to the sample whose metadata is being passed.
-         * @param cryptoInfo Encryption data required to decrypt the sample. May be null for
-         *     unencrypted samples. Implementors should treat any output {@link CryptoInfo}
-         *     instances as immutable. MediaParser will not modify any output {@code cryptoInfos}
-         *     and implementors should not modify them either.
-         */
-        void onSampleCompleted(
-                int trackIndex,
-                long timeMicros,
-                @SampleFlags int flags,
-                int size,
-                int offset,
-                @Nullable CryptoInfo cryptoInfo);
-    }
-
-    /**
-     * Thrown if all parser implementations provided to {@link #create} failed to sniff the input
-     * content.
-     */
-    public static final class UnrecognizedInputFormatException extends IOException {
-
-        /**
-         * Creates a new instance which signals that the parsers with the given names failed to
-         * parse the input.
-         */
-        @NonNull
-        @CheckResult
-        private static UnrecognizedInputFormatException createForExtractors(
-                @NonNull String... extractorNames) {
-            StringBuilder builder = new StringBuilder();
-            builder.append("None of the available parsers ( ");
-            builder.append(extractorNames[0]);
-            for (int i = 1; i < extractorNames.length; i++) {
-                builder.append(", ");
-                builder.append(extractorNames[i]);
-            }
-            builder.append(") could read the stream.");
-            return new UnrecognizedInputFormatException(builder.toString());
-        }
-
-        private UnrecognizedInputFormatException(String extractorNames) {
-            super(extractorNames);
-        }
-    }
-
-    /** Thrown when an error occurs while parsing a media stream. */
-    public static final class ParsingException extends IOException {
-
-        private ParsingException(ParserException cause) {
-            super(cause);
-        }
-    }
-
-    // Sample flags.
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-            flag = true,
-            value = {
-                SAMPLE_FLAG_KEY_FRAME,
-                SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA,
-                SAMPLE_FLAG_LAST_SAMPLE,
-                SAMPLE_FLAG_ENCRYPTED,
-                SAMPLE_FLAG_DECODE_ONLY
-            })
-    public @interface SampleFlags {}
-    /** Indicates that the sample holds a synchronization sample. */
-    public static final int SAMPLE_FLAG_KEY_FRAME = MediaCodec.BUFFER_FLAG_KEY_FRAME;
-    /**
-     * Indicates that the sample has supplemental data.
-     *
-     * <p>Samples will not have this flag set unless the {@code
-     * "android.media.mediaparser.includeSupplementalData"} parameter is set to {@code true} via
-     * {@link #setParameter}.
-     *
-     * <p>Samples with supplemental data have the following sample data format:
-     *
-     * <ul>
-     *   <li>If the {@code "android.media.mediaparser.inBandCryptoInfo"} parameter is set, all
-     *       encryption information.
-     *   <li>(4 bytes) {@code sample_data_size}: The size of the actual sample data, not including
-     *       supplemental data or encryption information.
-     *   <li>({@code sample_data_size} bytes): The media sample data.
-     *   <li>(remaining bytes) The supplemental data.
-     * </ul>
-     */
-    public static final int SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA = 1 << 28;
-    /** Indicates that the sample is known to contain the last media sample of the stream. */
-    public static final int SAMPLE_FLAG_LAST_SAMPLE = 1 << 29;
-    /** Indicates that the sample is (at least partially) encrypted. */
-    public static final int SAMPLE_FLAG_ENCRYPTED = 1 << 30;
-    /** Indicates that the sample should be decoded but not rendered. */
-    public static final int SAMPLE_FLAG_DECODE_ONLY = 1 << 31;
-
-    // Parser implementation names.
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @StringDef(
-            prefix = {"PARSER_NAME_"},
-            value = {
-                PARSER_NAME_UNKNOWN,
-                PARSER_NAME_MATROSKA,
-                PARSER_NAME_FMP4,
-                PARSER_NAME_MP4,
-                PARSER_NAME_MP3,
-                PARSER_NAME_ADTS,
-                PARSER_NAME_AC3,
-                PARSER_NAME_TS,
-                PARSER_NAME_FLV,
-                PARSER_NAME_OGG,
-                PARSER_NAME_PS,
-                PARSER_NAME_WAV,
-                PARSER_NAME_AMR,
-                PARSER_NAME_AC4,
-                PARSER_NAME_FLAC
-            })
-    public @interface ParserName {}
-
-    /** Parser name returned by {@link #getParserName()} when no parser has been selected yet. */
-    public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN";
-    /**
-     * Parser for the Matroska container format, as defined in the <a
-     * href="https://matroska.org/technical/specs/">spec</a>.
-     */
-    public static final String PARSER_NAME_MATROSKA = "android.media.mediaparser.MatroskaParser";
-    /**
-     * Parser for fragmented files using the MP4 container format, as defined in ISO/IEC 14496-12.
-     */
-    public static final String PARSER_NAME_FMP4 = "android.media.mediaparser.FragmentedMp4Parser";
-    /**
-     * Parser for non-fragmented files using the MP4 container format, as defined in ISO/IEC
-     * 14496-12.
-     */
-    public static final String PARSER_NAME_MP4 = "android.media.mediaparser.Mp4Parser";
-    /** Parser for the MP3 container format, as defined in ISO/IEC 11172-3. */
-    public static final String PARSER_NAME_MP3 = "android.media.mediaparser.Mp3Parser";
-    /** Parser for the ADTS container format, as defined in ISO/IEC 13818-7. */
-    public static final String PARSER_NAME_ADTS = "android.media.mediaparser.AdtsParser";
-    /**
-     * Parser for the AC-3 container format, as defined in Digital Audio Compression Standard
-     * (AC-3).
-     */
-    public static final String PARSER_NAME_AC3 = "android.media.mediaparser.Ac3Parser";
-    /** Parser for the TS container format, as defined in ISO/IEC 13818-1. */
-    public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser";
-    /**
-     * Parser for the FLV container format, as defined in Adobe Flash Video File Format
-     * Specification.
-     */
-    public static final String PARSER_NAME_FLV = "android.media.mediaparser.FlvParser";
-    /** Parser for the OGG container format, as defined in RFC 3533. */
-    public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser";
-    /** Parser for the PS container format, as defined in ISO/IEC 11172-1. */
-    public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser";
-    /**
-     * Parser for the WAV container format, as defined in Multimedia Programming Interface and Data
-     * Specifications.
-     */
-    public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser";
-    /** Parser for the AMR container format, as defined in RFC 4867. */
-    public static final String PARSER_NAME_AMR = "android.media.mediaparser.AmrParser";
-    /**
-     * Parser for the AC-4 container format, as defined by Dolby AC-4: Audio delivery for
-     * Next-Generation Entertainment Services.
-     */
-    public static final String PARSER_NAME_AC4 = "android.media.mediaparser.Ac4Parser";
-    /**
-     * Parser for the FLAC container format, as defined in the <a
-     * href="https://xiph.org/flac/">spec</a>.
-     */
-    public static final String PARSER_NAME_FLAC = "android.media.mediaparser.FlacParser";
-
-    // MediaParser parameters.
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @StringDef(
-            prefix = {"PARAMETER_"},
-            value = {
-                PARAMETER_ADTS_ENABLE_CBR_SEEKING,
-                PARAMETER_AMR_ENABLE_CBR_SEEKING,
-                PARAMETER_FLAC_DISABLE_ID3,
-                PARAMETER_MP4_IGNORE_EDIT_LISTS,
-                PARAMETER_MP4_IGNORE_TFDT_BOX,
-                PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES,
-                PARAMETER_MATROSKA_DISABLE_CUES_SEEKING,
-                PARAMETER_MP3_DISABLE_ID3,
-                PARAMETER_MP3_ENABLE_CBR_SEEKING,
-                PARAMETER_MP3_ENABLE_INDEX_SEEKING,
-                PARAMETER_TS_MODE,
-                PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES,
-                PARAMETER_TS_IGNORE_AAC_STREAM,
-                PARAMETER_TS_IGNORE_AVC_STREAM,
-                PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM,
-                PARAMETER_TS_DETECT_ACCESS_UNITS,
-                PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS,
-                PARAMETER_IN_BAND_CRYPTO_INFO,
-                PARAMETER_INCLUDE_SUPPLEMENTAL_DATA
-            })
-    public @interface ParameterName {}
-
-    /**
-     * Sets whether constant bitrate seeking should be enabled for ADTS parsing. {@code boolean}
-     * expected. Default value is {@code false}.
-     */
-    public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING =
-            "android.media.mediaparser.adts.enableCbrSeeking";
-    /**
-     * Sets whether constant bitrate seeking should be enabled for AMR. {@code boolean} expected.
-     * Default value is {@code false}.
-     */
-    public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING =
-            "android.media.mediaparser.amr.enableCbrSeeking";
-    /**
-     * Sets whether the ID3 track should be disabled for FLAC. {@code boolean} expected. Default
-     * value is {@code false}.
-     */
-    public static final String PARAMETER_FLAC_DISABLE_ID3 =
-            "android.media.mediaparser.flac.disableId3";
-    /**
-     * Sets whether MP4 parsing should ignore edit lists. {@code boolean} expected. Default value is
-     * {@code false}.
-     */
-    public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS =
-            "android.media.mediaparser.mp4.ignoreEditLists";
-    /**
-     * Sets whether MP4 parsing should ignore the tfdt box. {@code boolean} expected. Default value
-     * is {@code false}.
-     */
-    public static final String PARAMETER_MP4_IGNORE_TFDT_BOX =
-            "android.media.mediaparser.mp4.ignoreTfdtBox";
-    /**
-     * Sets whether MP4 parsing should treat all video frames as key frames. {@code boolean}
-     * expected. Default value is {@code false}.
-     */
-    public static final String PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES =
-            "android.media.mediaparser.mp4.treatVideoFramesAsKeyframes";
-    /**
-     * Sets whether Matroska parsing should avoid seeking to the cues element. {@code boolean}
-     * expected. Default value is {@code false}.
-     *
-     * <p>If this flag is enabled and the cues element occurs after the first cluster, then the
-     * media is treated as unseekable.
-     */
-    public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING =
-            "android.media.mediaparser.matroska.disableCuesSeeking";
-    /**
-     * Sets whether the ID3 track should be disabled for MP3. {@code boolean} expected. Default
-     * value is {@code false}.
-     */
-    public static final String PARAMETER_MP3_DISABLE_ID3 =
-            "android.media.mediaparser.mp3.disableId3";
-    /**
-     * Sets whether constant bitrate seeking should be enabled for MP3. {@code boolean} expected.
-     * Default value is {@code false}.
-     */
-    public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING =
-            "android.media.mediaparser.mp3.enableCbrSeeking";
-    /**
-     * Sets whether MP3 parsing should generate a time-to-byte mapping. {@code boolean} expected.
-     * Default value is {@code false}.
-     *
-     * <p>Enabling this flag may require to scan a significant portion of the file to compute a seek
-     * point. Therefore, it should only be used if:
-     *
-     * <ul>
-     *   <li>the file is small, or
-     *   <li>the bitrate is variable (or the type of bitrate is unknown) and the seeking metadata
-     *       provided in the file is not precise enough (or is not present).
-     * </ul>
-     */
-    public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING =
-            "android.media.mediaparser.mp3.enableIndexSeeking";
-    /**
-     * Sets the operation mode for TS parsing. {@code String} expected. Valid values are {@code
-     * "single_pmt"}, {@code "multi_pmt"}, and {@code "hls"}. Default value is {@code "single_pmt"}.
-     *
-     * <p>The operation modes alter the way TS behaves so that it can handle certain kinds of
-     * commonly-occurring malformed media.
-     *
-     * <ul>
-     *   <li>{@code "single_pmt"}: Only the first found PMT is parsed. Others are ignored, even if
-     *       more PMTs are declared in the PAT.
-     *   <li>{@code "multi_pmt"}: Behave as described in ISO/IEC 13818-1.
-     *   <li>{@code "hls"}: Enable {@code "single_pmt"} mode, and ignore continuity counters.
-     * </ul>
-     */
-    public static final String PARAMETER_TS_MODE = "android.media.mediaparser.ts.mode";
-    /**
-     * Sets whether TS should treat samples consisting of non-IDR I slices as synchronization
-     * samples (key-frames). {@code boolean} expected. Default value is {@code false}.
-     */
-    public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES =
-            "android.media.mediaparser.ts.allowNonIdrAvcKeyframes";
-    /**
-     * Sets whether TS parsing should ignore AAC elementary streams. {@code boolean} expected.
-     * Default value is {@code false}.
-     */
-    public static final String PARAMETER_TS_IGNORE_AAC_STREAM =
-            "android.media.mediaparser.ts.ignoreAacStream";
-    /**
-     * Sets whether TS parsing should ignore AVC elementary streams. {@code boolean} expected.
-     * Default value is {@code false}.
-     */
-    public static final String PARAMETER_TS_IGNORE_AVC_STREAM =
-            "android.media.mediaparser.ts.ignoreAvcStream";
-    /**
-     * Sets whether TS parsing should ignore splice information streams. {@code boolean} expected.
-     * Default value is {@code false}.
-     */
-    public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM =
-            "android.media.mediaparser.ts.ignoreSpliceInfoStream";
-    /**
-     * Sets whether TS parsing should split AVC stream into access units based on slice headers.
-     * {@code boolean} expected. Default value is {@code false}.
-     *
-     * <p>This flag should be left disabled if the stream contains access units delimiters in order
-     * to avoid unnecessary computational costs.
-     */
-    public static final String PARAMETER_TS_DETECT_ACCESS_UNITS =
-            "android.media.mediaparser.ts.ignoreDetectAccessUnits";
-    /**
-     * Sets whether TS parsing should handle HDMV DTS audio streams. {@code boolean} expected.
-     * Default value is {@code false}.
-     *
-     * <p>Enabling this flag will disable the detection of SCTE subtitles.
-     */
-    public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS =
-            "android.media.mediaparser.ts.enableHdmvDtsAudioStreams";
-    /**
-     * Sets whether encryption data should be sent in-band with the sample data, as per {@link
-     * OutputConsumer#onSampleDataFound}. {@code boolean} expected. Default value is {@code false}.
-     *
-     * <p>If this parameter is set, encrypted samples' data will be prefixed with the encryption
-     * information bytes. The format for in-band encryption information is:
-     *
-     * <ul>
-     *   <li>(1 byte) {@code encryption_signal_byte}: Most significant bit signals whether the
-     *       encryption data contains subsample encryption data. The remaining bits contain {@code
-     *       initialization_vector_size}.
-     *   <li>({@code initialization_vector_size} bytes) Initialization vector.
-     *   <li>If subsample encryption data is present, as per {@code encryption_signal_byte}, the
-     *       encryption data also contains:
-     *       <ul>
-     *         <li>(2 bytes) {@code subsample_encryption_data_length}.
-     *         <li>({@code subsample_encryption_data_length * 6} bytes) Subsample encryption data
-     *             (repeated {@code subsample_encryption_data_length} times):
-     *             <ul>
-     *               <li>(2 bytes) Size of a clear section in sample.
-     *               <li>(4 bytes) Size of an encryption section in sample.
-     *             </ul>
-     *       </ul>
-     * </ul>
-     *
-     * @hide
-     */
-    public static final String PARAMETER_IN_BAND_CRYPTO_INFO =
-            "android.media.mediaparser.inBandCryptoInfo";
-    /**
-     * Sets whether supplemental data should be included as part of the sample data. {@code boolean}
-     * expected. Default value is {@code false}. See {@link #SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA} for
-     * information about the sample data format.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_INCLUDE_SUPPLEMENTAL_DATA =
-            "android.media.mediaparser.includeSupplementalData";
-    /**
-     * Sets whether sample timestamps may start from non-zero offsets. {@code boolean} expected.
-     * Default value is {@code false}.
-     *
-     * <p>When set to true, sample timestamps will not be offset to start from zero, and the media
-     * provided timestamps will be used instead. For example, transport stream sample timestamps
-     * will not be converted to a zero-based timebase.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_IGNORE_TIMESTAMP_OFFSET =
-            "android.media.mediaparser.ignoreTimestampOffset";
-    /**
-     * Sets whether each track type should be eagerly exposed. {@code boolean} expected. Default
-     * value is {@code false}.
-     *
-     * <p>When set to true, each track type will be eagerly exposed through a call to {@link
-     * OutputConsumer#onTrackDataFound} containing a single-value {@link MediaFormat}. The key for
-     * the track type is {@code "track-type-string"}, and the possible values are {@code "video"},
-     * {@code "audio"}, {@code "text"}, {@code "metadata"}, and {@code "unknown"}.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_EAGERLY_EXPOSE_TRACKTYPE =
-            "android.media.mediaparser.eagerlyExposeTrackType";
-    /**
-     * Sets whether a dummy {@link SeekMap} should be exposed before starting extraction. {@code
-     * boolean} expected. Default value is {@code false}.
-     *
-     * <p>For each {@link SeekMap#getSeekPoints} call, the dummy {@link SeekMap} returns a single
-     * {@link SeekPoint} whose {@link SeekPoint#timeMicros} matches the requested timestamp, and
-     * whose {@link SeekPoint#position} is 0.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_EXPOSE_DUMMY_SEEKMAP =
-            "android.media.mediaparser.exposeDummySeekMap";
-
-    /**
-     * Sets whether chunk indices available in the extracted media should be exposed as {@link
-     * MediaFormat MediaFormats}. {@code boolean} expected. Default value is {@link false}.
-     *
-     * <p>When set to true, any information about media segmentation will be exposed as a {@link
-     * MediaFormat} (with track index 0) containing four {@link ByteBuffer} elements under the
-     * following keys:
-     *
-     * <ul>
-     *   <li>"chunk-index-int-sizes": Contains {@code ints} representing the sizes in bytes of each
-     *       of the media segments.
-     *   <li>"chunk-index-long-offsets": Contains {@code longs} representing the byte offsets of
-     *       each segment in the stream.
-     *   <li>"chunk-index-long-us-durations": Contains {@code longs} representing the media duration
-     *       of each segment, in microseconds.
-     *   <li>"chunk-index-long-us-times": Contains {@code longs} representing the start time of each
-     *       segment, in microseconds.
-     * </ul>
-     *
-     * @hide
-     */
-    public static final String PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT =
-            "android.media.mediaParser.exposeChunkIndexAsMediaFormat";
-    /**
-     * Sets a list of closed-caption {@link MediaFormat MediaFormats} that should be exposed as part
-     * of the extracted media. {@code List<MediaFormat>} expected. Default value is an empty list.
-     *
-     * <p>Expected keys in the {@link MediaFormat} are:
-     *
-     * <ul>
-     *   <p>{@link MediaFormat#KEY_MIME}: Determine the type of captions (for example,
-     *   application/cea-608). Mandatory.
-     *   <p>{@link MediaFormat#KEY_CAPTION_SERVICE_NUMBER}: Determine the channel on which the
-     *   captions are transmitted. Optional.
-     * </ul>
-     *
-     * @hide
-     */
-    public static final String PARAMETER_EXPOSE_CAPTION_FORMATS =
-            "android.media.mediaParser.exposeCaptionFormats";
-    /**
-     * Sets whether the value associated with {@link #PARAMETER_EXPOSE_CAPTION_FORMATS} should
-     * override any in-band caption service declarations. {@code boolean} expected. Default value is
-     * {@link false}.
-     *
-     * <p>When {@code false}, any present in-band caption services information will override the
-     * values associated with {@link #PARAMETER_EXPOSE_CAPTION_FORMATS}.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS =
-            "android.media.mediaParser.overrideInBandCaptionDeclarations";
-    /**
-     * Sets whether a track for EMSG events should be exposed in case of parsing a container that
-     * supports them. {@code boolean} expected. Default value is {@link false}.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_EXPOSE_EMSG_TRACK =
-            "android.media.mediaParser.exposeEmsgTrack";
-
-    // Private constants.
-
-    private static final String TAG = "MediaParser";
-    private static final String JNI_LIBRARY_NAME = "mediaparser-jni";
-    private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME;
-    private static final Map<String, Class> EXPECTED_TYPE_BY_PARAMETER_NAME;
-    private static final String TS_MODE_SINGLE_PMT = "single_pmt";
-    private static final String TS_MODE_MULTI_PMT = "multi_pmt";
-    private static final String TS_MODE_HLS = "hls";
-    private static final int BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY = 6;
-    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
-    private static final String MEDIAMETRICS_ELEMENT_SEPARATOR = "|";
-    private static final int MEDIAMETRICS_MAX_STRING_SIZE = 200;
-    private static final int MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH;
-    /**
-     * Intentional error introduced to reported metrics to prevent identification of the parsed
-     * media. Note: Increasing this value may cause older hostside CTS tests to fail.
-     */
-    private static final float MEDIAMETRICS_DITHER = .02f;
-
-    @IntDef(
-            value = {
-                STATE_READING_SIGNAL_BYTE,
-                STATE_READING_INIT_VECTOR,
-                STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE,
-                STATE_READING_SUBSAMPLE_ENCRYPTION_DATA
-            })
-    private @interface EncryptionDataReadState {}
-
-    private static final int STATE_READING_SIGNAL_BYTE = 0;
-    private static final int STATE_READING_INIT_VECTOR = 1;
-    private static final int STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE = 2;
-    private static final int STATE_READING_SUBSAMPLE_ENCRYPTION_DATA = 3;
-
-    // Instance creation methods.
-
-    /**
-     * Creates an instance backed by the parser with the given {@code name}. The returned instance
-     * will attempt parsing without sniffing the content.
-     *
-     * @param name The name of the parser that will be associated with the created instance.
-     * @param outputConsumer The {@link OutputConsumer} to which track data and samples are pushed.
-     * @return A new instance.
-     * @throws IllegalArgumentException If an invalid name is provided.
-     */
-    @NonNull
-    public static MediaParser createByName(
-            @NonNull @ParserName String name, @NonNull OutputConsumer outputConsumer) {
-        String[] nameAsArray = new String[] {name};
-        assertValidNames(nameAsArray);
-        return new MediaParser(outputConsumer, /* createdByName= */ true, name);
-    }
-
-    /**
-     * Creates an instance whose backing parser will be selected by sniffing the content during the
-     * first {@link #advance} call. Parser implementations will sniff the content in order of
-     * appearance in {@code parserNames}.
-     *
-     * @param outputConsumer The {@link OutputConsumer} to which extracted data is output.
-     * @param parserNames The names of the parsers to sniff the content with. If empty, a default
-     *     array of names is used.
-     * @return A new instance.
-     */
-    @NonNull
-    public static MediaParser create(
-            @NonNull OutputConsumer outputConsumer, @NonNull @ParserName String... parserNames) {
-        assertValidNames(parserNames);
-        if (parserNames.length == 0) {
-            parserNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]);
-        }
-        return new MediaParser(outputConsumer, /* createdByName= */ false, parserNames);
-    }
-
-    // Misc static methods.
-
-    /**
-     * Returns an immutable list with the names of the parsers that are suitable for container
-     * formats with the given {@link MediaFormat}.
-     *
-     * <p>A parser supports a {@link MediaFormat} if the mime type associated with {@link
-     * MediaFormat#KEY_MIME} corresponds to the supported container format.
-     *
-     * @param mediaFormat The {@link MediaFormat} to check support for.
-     * @return The parser names that support the given {@code mediaFormat}, or the list of all
-     *     parsers available if no container specific format information is provided.
-     */
-    @NonNull
-    @ParserName
-    public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) {
-        String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
-        mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim());
-        if (TextUtils.isEmpty(mimeType)) {
-            // No MIME type provided. Return all.
-            return Collections.unmodifiableList(
-                    new ArrayList<>(EXTRACTOR_FACTORIES_BY_NAME.keySet()));
-        }
-        ArrayList<String> result = new ArrayList<>();
-        switch (mimeType) {
-            case "video/x-matroska":
-            case "audio/x-matroska":
-            case "video/x-webm":
-            case "audio/x-webm":
-                result.add(PARSER_NAME_MATROSKA);
-                break;
-            case "video/mp4":
-            case "audio/mp4":
-            case "application/mp4":
-                result.add(PARSER_NAME_MP4);
-                result.add(PARSER_NAME_FMP4);
-                break;
-            case "audio/mpeg":
-                result.add(PARSER_NAME_MP3);
-                break;
-            case "audio/aac":
-                result.add(PARSER_NAME_ADTS);
-                break;
-            case "audio/ac3":
-                result.add(PARSER_NAME_AC3);
-                break;
-            case "video/mp2t":
-            case "audio/mp2t":
-                result.add(PARSER_NAME_TS);
-                break;
-            case "video/x-flv":
-                result.add(PARSER_NAME_FLV);
-                break;
-            case "video/ogg":
-            case "audio/ogg":
-            case "application/ogg":
-                result.add(PARSER_NAME_OGG);
-                break;
-            case "video/mp2p":
-            case "video/mp1s":
-                result.add(PARSER_NAME_PS);
-                break;
-            case "audio/vnd.wave":
-            case "audio/wav":
-            case "audio/wave":
-            case "audio/x-wav":
-                result.add(PARSER_NAME_WAV);
-                break;
-            case "audio/amr":
-                result.add(PARSER_NAME_AMR);
-                break;
-            case "audio/ac4":
-                result.add(PARSER_NAME_AC4);
-                break;
-            case "audio/flac":
-            case "audio/x-flac":
-                result.add(PARSER_NAME_FLAC);
-                break;
-            default:
-                // No parsers support the given mime type. Do nothing.
-                break;
-        }
-        return Collections.unmodifiableList(result);
-    }
-
-    // Private fields.
-
-    private final Map<String, Object> mParserParameters;
-    private final OutputConsumer mOutputConsumer;
-    private final String[] mParserNamesPool;
-    private final PositionHolder mPositionHolder;
-    private final InputReadingDataReader mExoDataReader;
-    private final DataReaderAdapter mScratchDataReaderAdapter;
-    private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
-    @Nullable private final Constructor<DrmInitData.SchemeInitData> mSchemeInitDataConstructor;
-    private final ArrayList<Format> mMuxedCaptionFormats;
-    private boolean mInBandCryptoInfo;
-    private boolean mIncludeSupplementalData;
-    private boolean mIgnoreTimestampOffset;
-    private boolean mEagerlyExposeTrackType;
-    private boolean mExposeDummySeekMap;
-    private boolean mExposeChunkIndexAsMediaFormat;
-    private String mParserName;
-    private Extractor mExtractor;
-    private ExtractorInput mExtractorInput;
-    private boolean mPendingExtractorInit;
-    private long mPendingSeekPosition;
-    private long mPendingSeekTimeMicros;
-    private boolean mLoggedSchemeInitDataCreationException;
-    private boolean mReleased;
-
-    // MediaMetrics fields.
-    @Nullable private LogSessionId mLogSessionId;
-    private final boolean mCreatedByName;
-    private final SparseArray<Format> mTrackFormats;
-    private String mLastObservedExceptionName;
-    private long mDurationMillis;
-    private long mResourceByteCount;
-
-    // Public methods.
-
-    /**
-     * Sets parser-specific parameters which allow customizing behavior.
-     *
-     * <p>Must be called before the first call to {@link #advance}.
-     *
-     * @param parameterName The name of the parameter to set. See {@code PARAMETER_*} constants for
-     *     documentation on possible values.
-     * @param value The value to set for the given {@code parameterName}. See {@code PARAMETER_*}
-     *     constants for documentation on the expected types.
-     * @return This instance, for convenience.
-     * @throws IllegalStateException If called after calling {@link #advance} on the same instance.
-     */
-    @NonNull
-    public MediaParser setParameter(
-            @NonNull @ParameterName String parameterName, @NonNull Object value) {
-        if (mExtractor != null) {
-            throw new IllegalStateException(
-                    "setParameters() must be called before the first advance() call.");
-        }
-        Class expectedType = EXPECTED_TYPE_BY_PARAMETER_NAME.get(parameterName);
-        // Ignore parameter names that are not contained in the map, in case the client is passing
-        // a parameter that is being added in a future version of this library.
-        if (expectedType != null && !expectedType.isInstance(value)) {
-            throw new IllegalArgumentException(
-                    parameterName
-                            + " expects a "
-                            + expectedType.getSimpleName()
-                            + " but a "
-                            + value.getClass().getSimpleName()
-                            + " was passed.");
-        }
-        if (PARAMETER_TS_MODE.equals(parameterName)
-                && !TS_MODE_SINGLE_PMT.equals(value)
-                && !TS_MODE_HLS.equals(value)
-                && !TS_MODE_MULTI_PMT.equals(value)) {
-            throw new IllegalArgumentException(PARAMETER_TS_MODE + " does not accept: " + value);
-        }
-        if (PARAMETER_IN_BAND_CRYPTO_INFO.equals(parameterName)) {
-            mInBandCryptoInfo = (boolean) value;
-        }
-        if (PARAMETER_INCLUDE_SUPPLEMENTAL_DATA.equals(parameterName)) {
-            mIncludeSupplementalData = (boolean) value;
-        }
-        if (PARAMETER_IGNORE_TIMESTAMP_OFFSET.equals(parameterName)) {
-            mIgnoreTimestampOffset = (boolean) value;
-        }
-        if (PARAMETER_EAGERLY_EXPOSE_TRACKTYPE.equals(parameterName)) {
-            mEagerlyExposeTrackType = (boolean) value;
-        }
-        if (PARAMETER_EXPOSE_DUMMY_SEEKMAP.equals(parameterName)) {
-            mExposeDummySeekMap = (boolean) value;
-        }
-        if (PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT.equals(parameterName)) {
-            mExposeChunkIndexAsMediaFormat = (boolean) value;
-        }
-        if (PARAMETER_EXPOSE_CAPTION_FORMATS.equals(parameterName)) {
-            setMuxedCaptionFormats((List<MediaFormat>) value);
-        }
-        mParserParameters.put(parameterName, value);
-        return this;
-    }
-
-    /**
-     * Returns whether the given {@code parameterName} is supported by this parser.
-     *
-     * @param parameterName The parameter name to check support for. One of the {@code PARAMETER_*}
-     *     constants.
-     * @return Whether the given {@code parameterName} is supported.
-     */
-    public boolean supportsParameter(@NonNull @ParameterName String parameterName) {
-        return EXPECTED_TYPE_BY_PARAMETER_NAME.containsKey(parameterName);
-    }
-
-    /**
-     * Returns the name of the backing parser implementation.
-     *
-     * <p>If this instance was creating using {@link #createByName}, the provided name is returned.
-     * If this instance was created using {@link #create}, this method will return {@link
-     * #PARSER_NAME_UNKNOWN} until the first call to {@link #advance}, after which the name of the
-     * backing parser implementation is returned.
-     *
-     * @return The name of the backing parser implementation, or null if the backing parser
-     *     implementation has not yet been selected.
-     */
-    @NonNull
-    @ParserName
-    public String getParserName() {
-        return mParserName;
-    }
-
-    /**
-     * Makes progress in the extraction of the input media stream, unless the end of the input has
-     * been reached.
-     *
-     * <p>This method will block until some progress has been made.
-     *
-     * <p>If this instance was created using {@link #create}, the first call to this method will
-     * sniff the content using the selected parser implementations.
-     *
-     * @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media
-     *     container data.
-     * @return Whether there is any data left to extract. Returns false if the end of input has been
-     *     reached.
-     * @throws IOException If an error occurs while reading from the {@link SeekableInputReader}.
-     * @throws UnrecognizedInputFormatException If the format cannot be recognized by any of the
-     *     underlying parser implementations.
-     */
-    public boolean advance(@NonNull SeekableInputReader seekableInputReader) throws IOException {
-        if (mExtractorInput == null) {
-            // TODO: For efficiency, the same implementation should be used, by providing a
-            // clearBuffers() method, or similar.
-            long resourceLength = seekableInputReader.getLength();
-            if (mResourceByteCount == 0) {
-                // For resource byte count metric collection, we only take into account the length
-                // of the first provided input reader.
-                mResourceByteCount = resourceLength;
-            }
-            mExtractorInput =
-                    new DefaultExtractorInput(
-                            mExoDataReader, seekableInputReader.getPosition(), resourceLength);
-        }
-        mExoDataReader.mInputReader = seekableInputReader;
-
-        if (mExtractor == null) {
-            mPendingExtractorInit = true;
-            if (!mParserName.equals(PARSER_NAME_UNKNOWN)) {
-                mExtractor = createExtractor(mParserName);
-            } else {
-                for (String parserName : mParserNamesPool) {
-                    Extractor extractor = createExtractor(parserName);
-                    try {
-                        if (extractor.sniff(mExtractorInput)) {
-                            mParserName = parserName;
-                            mExtractor = extractor;
-                            mPendingExtractorInit = true;
-                            break;
-                        }
-                    } catch (EOFException e) {
-                        // Do nothing.
-                    } finally {
-                        mExtractorInput.resetPeekPosition();
-                    }
-                }
-                if (mExtractor == null) {
-                    UnrecognizedInputFormatException exception =
-                            UnrecognizedInputFormatException.createForExtractors(mParserNamesPool);
-                    mLastObservedExceptionName = exception.getClass().getName();
-                    throw exception;
-                }
-                return true;
-            }
-        }
-
-        if (mPendingExtractorInit) {
-            if (mExposeDummySeekMap) {
-                // We propagate the dummy seek map before initializing the extractor, in case the
-                // extractor initialization outputs a seek map.
-                mOutputConsumer.onSeekMapFound(SeekMap.DUMMY);
-            }
-            mExtractor.init(new ExtractorOutputAdapter());
-            mPendingExtractorInit = false;
-            // We return after initialization to allow clients use any output information before
-            // starting actual extraction.
-            return true;
-        }
-
-        if (isPendingSeek()) {
-            mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeMicros);
-            removePendingSeek();
-        }
-
-        mPositionHolder.position = seekableInputReader.getPosition();
-        int result;
-        try {
-            result = mExtractor.read(mExtractorInput, mPositionHolder);
-        } catch (Exception e) {
-            mLastObservedExceptionName = e.getClass().getName();
-            if (e instanceof ParserException) {
-                throw new ParsingException((ParserException) e);
-            } else {
-                throw e;
-            }
-        }
-        if (result == Extractor.RESULT_END_OF_INPUT) {
-            mExtractorInput = null;
-            return false;
-        }
-        if (result == Extractor.RESULT_SEEK) {
-            mExtractorInput = null;
-            seekableInputReader.seekToPosition(mPositionHolder.position);
-        }
-        return true;
-    }
-
-    /**
-     * Seeks within the media container being extracted.
-     *
-     * <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link
-     * OutputConsumer#onSeekMapFound(SeekMap)}.
-     *
-     * <p>Following a call to this method, the {@link InputReader} passed to the next invocation of
-     * {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream.
-     *
-     * @param seekPoint The {@link SeekPoint} to seek to.
-     */
-    public void seek(@NonNull SeekPoint seekPoint) {
-        if (mExtractor == null) {
-            mPendingSeekPosition = seekPoint.position;
-            mPendingSeekTimeMicros = seekPoint.timeMicros;
-        } else {
-            mExtractor.seek(seekPoint.position, seekPoint.timeMicros);
-        }
-    }
-
-    /**
-     * Releases any acquired resources.
-     *
-     * <p>After calling this method, this instance becomes unusable and no other methods should be
-     * invoked.
-     */
-    public void release() {
-        mExtractorInput = null;
-        mExtractor = null;
-        if (mReleased) {
-            // Nothing to do.
-            return;
-        }
-        mReleased = true;
-
-        String trackMimeTypes = buildMediaMetricsString(format -> format.sampleMimeType);
-        String trackCodecs = buildMediaMetricsString(format -> format.codecs);
-        int videoWidth = -1;
-        int videoHeight = -1;
-        for (int i = 0; i < mTrackFormats.size(); i++) {
-            Format format = mTrackFormats.valueAt(i);
-            if (format.width != Format.NO_VALUE && format.height != Format.NO_VALUE) {
-                videoWidth = format.width;
-                videoHeight = format.height;
-                break;
-            }
-        }
-
-        String alteredParameters =
-                String.join(
-                        MEDIAMETRICS_ELEMENT_SEPARATOR,
-                        mParserParameters.keySet().toArray(new String[0]));
-        alteredParameters =
-                alteredParameters.substring(
-                        0,
-                        Math.min(
-                                alteredParameters.length(),
-                                MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH));
-
-        nativeSubmitMetrics(
-                SdkLevel.isAtLeastS() ? getLogSessionIdStringV31() : "",
-                mParserName,
-                mCreatedByName,
-                String.join(MEDIAMETRICS_ELEMENT_SEPARATOR, mParserNamesPool),
-                mLastObservedExceptionName,
-                addDither(mResourceByteCount),
-                addDither(mDurationMillis),
-                trackMimeTypes,
-                trackCodecs,
-                alteredParameters,
-                videoWidth,
-                videoHeight);
-    }
-
-    @RequiresApi(31)
-    public void setLogSessionId(@NonNull LogSessionId logSessionId) {
-        this.mLogSessionId = Objects.requireNonNull(logSessionId);
-    }
-
-    @RequiresApi(31)
-    @NonNull
-    public LogSessionId getLogSessionId() {
-        return mLogSessionId != null ? mLogSessionId : LogSessionId.LOG_SESSION_ID_NONE;
-    }
-
-    // Private methods.
-
-    private MediaParser(
-            OutputConsumer outputConsumer, boolean createdByName, String... parserNamesPool) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
-            throw new UnsupportedOperationException("Android version must be R or greater.");
-        }
-        mParserParameters = new HashMap<>();
-        mOutputConsumer = outputConsumer;
-        mParserNamesPool = parserNamesPool;
-        mCreatedByName = createdByName;
-        mParserName = createdByName ? parserNamesPool[0] : PARSER_NAME_UNKNOWN;
-        mPositionHolder = new PositionHolder();
-        mExoDataReader = new InputReadingDataReader();
-        removePendingSeek();
-        mScratchDataReaderAdapter = new DataReaderAdapter();
-        mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter();
-        mSchemeInitDataConstructor = getSchemeInitDataConstructor();
-        mMuxedCaptionFormats = new ArrayList<>();
-
-        // MediaMetrics.
-        mTrackFormats = new SparseArray<>();
-        mLastObservedExceptionName = "";
-        mDurationMillis = -1;
-    }
-
-    private String buildMediaMetricsString(Function<Format, String> formatFieldGetter) {
-        StringBuilder stringBuilder = new StringBuilder();
-        for (int i = 0; i < mTrackFormats.size(); i++) {
-            if (i > 0) {
-                stringBuilder.append(MEDIAMETRICS_ELEMENT_SEPARATOR);
-            }
-            String fieldValue = formatFieldGetter.apply(mTrackFormats.valueAt(i));
-            stringBuilder.append(fieldValue != null ? fieldValue : "");
-        }
-        return stringBuilder.substring(
-                0, Math.min(stringBuilder.length(), MEDIAMETRICS_MAX_STRING_SIZE));
-    }
-
-    private void setMuxedCaptionFormats(List<MediaFormat> mediaFormats) {
-        mMuxedCaptionFormats.clear();
-        for (MediaFormat mediaFormat : mediaFormats) {
-            mMuxedCaptionFormats.add(toExoPlayerCaptionFormat(mediaFormat));
-        }
-    }
-
-    private boolean isPendingSeek() {
-        return mPendingSeekPosition >= 0;
-    }
-
-    private void removePendingSeek() {
-        mPendingSeekPosition = -1;
-        mPendingSeekTimeMicros = -1;
-    }
-
-    private Extractor createExtractor(String parserName) {
-        int flags = 0;
-        TimestampAdjuster timestampAdjuster = null;
-        if (mIgnoreTimestampOffset) {
-            timestampAdjuster = new TimestampAdjuster(TimestampAdjuster.DO_NOT_OFFSET);
-        }
-        switch (parserName) {
-            case PARSER_NAME_MATROSKA:
-                flags =
-                        getBooleanParameter(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING)
-                                ? MatroskaExtractor.FLAG_DISABLE_SEEK_FOR_CUES
-                                : 0;
-                return new MatroskaExtractor(flags);
-            case PARSER_NAME_FMP4:
-                flags |=
-                        getBooleanParameter(PARAMETER_EXPOSE_EMSG_TRACK)
-                                ? FragmentedMp4Extractor.FLAG_ENABLE_EMSG_TRACK
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
-                                ? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_MP4_IGNORE_TFDT_BOX)
-                                ? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES)
-                                ? FragmentedMp4Extractor
-                                        .FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME
-                                : 0;
-                return new FragmentedMp4Extractor(
-                        flags,
-                        timestampAdjuster,
-                        /* sideloadedTrack= */ null,
-                        mMuxedCaptionFormats);
-            case PARSER_NAME_MP4:
-                flags |=
-                        getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
-                                ? Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
-                                : 0;
-                return new Mp4Extractor(flags);
-            case PARSER_NAME_MP3:
-                flags |=
-                        getBooleanParameter(PARAMETER_MP3_DISABLE_ID3)
-                                ? Mp3Extractor.FLAG_DISABLE_ID3_METADATA
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_MP3_ENABLE_CBR_SEEKING)
-                                ? Mp3Extractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
-                                : 0;
-                // TODO: Add index seeking once we update the ExoPlayer version.
-                return new Mp3Extractor(flags);
-            case PARSER_NAME_ADTS:
-                flags |=
-                        getBooleanParameter(PARAMETER_ADTS_ENABLE_CBR_SEEKING)
-                                ? AdtsExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
-                                : 0;
-                return new AdtsExtractor(flags);
-            case PARSER_NAME_AC3:
-                return new Ac3Extractor();
-            case PARSER_NAME_TS:
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES)
-                                ? DefaultTsPayloadReaderFactory.FLAG_ALLOW_NON_IDR_KEYFRAMES
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_DETECT_ACCESS_UNITS)
-                                ? DefaultTsPayloadReaderFactory.FLAG_DETECT_ACCESS_UNITS
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS)
-                                ? DefaultTsPayloadReaderFactory.FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_IGNORE_AAC_STREAM)
-                                ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_AAC_STREAM
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_IGNORE_AVC_STREAM)
-                                ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_H264_STREAM
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM)
-                                ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS)
-                                ? DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS
-                                : 0;
-                String tsMode = getStringParameter(PARAMETER_TS_MODE, TS_MODE_SINGLE_PMT);
-                int hlsMode =
-                        TS_MODE_SINGLE_PMT.equals(tsMode)
-                                ? TsExtractor.MODE_SINGLE_PMT
-                                : TS_MODE_HLS.equals(tsMode)
-                                        ? TsExtractor.MODE_HLS
-                                        : TsExtractor.MODE_MULTI_PMT;
-                return new TsExtractor(
-                        hlsMode,
-                        timestampAdjuster != null
-                                ? timestampAdjuster
-                                : new TimestampAdjuster(/* firstSampleTimestampUs= */ 0),
-                        new DefaultTsPayloadReaderFactory(flags, mMuxedCaptionFormats));
-            case PARSER_NAME_FLV:
-                return new FlvExtractor();
-            case PARSER_NAME_OGG:
-                return new OggExtractor();
-            case PARSER_NAME_PS:
-                return new PsExtractor();
-            case PARSER_NAME_WAV:
-                return new WavExtractor();
-            case PARSER_NAME_AMR:
-                flags |=
-                        getBooleanParameter(PARAMETER_AMR_ENABLE_CBR_SEEKING)
-                                ? AmrExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
-                                : 0;
-                return new AmrExtractor(flags);
-            case PARSER_NAME_AC4:
-                return new Ac4Extractor();
-            case PARSER_NAME_FLAC:
-                flags |=
-                        getBooleanParameter(PARAMETER_FLAC_DISABLE_ID3)
-                                ? FlacExtractor.FLAG_DISABLE_ID3_METADATA
-                                : 0;
-                return new FlacExtractor(flags);
-            default:
-                // Should never happen.
-                throw new IllegalStateException("Unexpected attempt to create: " + parserName);
-        }
-    }
-
-    private boolean getBooleanParameter(String name) {
-        return (boolean) mParserParameters.getOrDefault(name, false);
-    }
-
-    private String getStringParameter(String name, String defaultValue) {
-        return (String) mParserParameters.getOrDefault(name, defaultValue);
-    }
-
-    @RequiresApi(31)
-    private String getLogSessionIdStringV31() {
-        return mLogSessionId != null ? mLogSessionId.getStringId() : "";
-    }
-
-    // Private classes.
-
-    private static final class InputReadingDataReader implements DataReader {
-
-        public InputReader mInputReader;
-
-        @Override
-        public int read(byte[] buffer, int offset, int readLength) throws IOException {
-            return mInputReader.read(buffer, offset, readLength);
-        }
-    }
-
-    private final class MediaParserDrmInitData extends DrmInitData {
-
-        private final SchemeInitData[] mSchemeDatas;
-
-        private MediaParserDrmInitData(com.google.android.exoplayer2.drm.DrmInitData exoDrmInitData)
-                throws IllegalAccessException, InstantiationException, InvocationTargetException {
-            mSchemeDatas = new SchemeInitData[exoDrmInitData.schemeDataCount];
-            for (int i = 0; i < mSchemeDatas.length; i++) {
-                mSchemeDatas[i] = toFrameworkSchemeInitData(exoDrmInitData.get(i));
-            }
-        }
-
-        @Override
-        @Nullable
-        public SchemeInitData get(UUID schemeUuid) {
-            for (SchemeInitData schemeInitData : mSchemeDatas) {
-                if (schemeInitData.uuid.equals(schemeUuid)) {
-                    return schemeInitData;
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public SchemeInitData getSchemeInitDataAt(int index) {
-            return mSchemeDatas[index];
-        }
-
-        @Override
-        public int getSchemeInitDataCount() {
-            return mSchemeDatas.length;
-        }
-
-        private DrmInitData.SchemeInitData toFrameworkSchemeInitData(SchemeData exoSchemeData)
-                throws IllegalAccessException, InvocationTargetException, InstantiationException {
-            return mSchemeInitDataConstructor.newInstance(
-                    exoSchemeData.uuid, exoSchemeData.mimeType, exoSchemeData.data);
-        }
-    }
-
-    private final class ExtractorOutputAdapter implements ExtractorOutput {
-
-        private final SparseArray<TrackOutput> mTrackOutputAdapters;
-        private boolean mTracksEnded;
-
-        private ExtractorOutputAdapter() {
-            mTrackOutputAdapters = new SparseArray<>();
-        }
-
-        @Override
-        public TrackOutput track(int id, int type) {
-            TrackOutput trackOutput = mTrackOutputAdapters.get(id);
-            if (trackOutput == null) {
-                int trackIndex = mTrackOutputAdapters.size();
-                trackOutput = new TrackOutputAdapter(trackIndex);
-                mTrackOutputAdapters.put(id, trackOutput);
-                if (mEagerlyExposeTrackType) {
-                    MediaFormat mediaFormat = new MediaFormat();
-                    mediaFormat.setString("track-type-string", toTypeString(type));
-                    mOutputConsumer.onTrackDataFound(
-                            trackIndex, new TrackData(mediaFormat, /* drmInitData= */ null));
-                }
-            }
-            return trackOutput;
-        }
-
-        @Override
-        public void endTracks() {
-            mOutputConsumer.onTrackCountFound(mTrackOutputAdapters.size());
-        }
-
-        @Override
-        public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
-            long durationUs = exoplayerSeekMap.getDurationUs();
-            if (durationUs != C.TIME_UNSET) {
-                mDurationMillis = C.usToMs(durationUs);
-            }
-            if (mExposeChunkIndexAsMediaFormat && exoplayerSeekMap instanceof ChunkIndex) {
-                ChunkIndex chunkIndex = (ChunkIndex) exoplayerSeekMap;
-                MediaFormat mediaFormat = new MediaFormat();
-                mediaFormat.setByteBuffer("chunk-index-int-sizes", toByteBuffer(chunkIndex.sizes));
-                mediaFormat.setByteBuffer(
-                        "chunk-index-long-offsets", toByteBuffer(chunkIndex.offsets));
-                mediaFormat.setByteBuffer(
-                        "chunk-index-long-us-durations", toByteBuffer(chunkIndex.durationsUs));
-                mediaFormat.setByteBuffer(
-                        "chunk-index-long-us-times", toByteBuffer(chunkIndex.timesUs));
-                mOutputConsumer.onTrackDataFound(
-                        /* trackIndex= */ 0, new TrackData(mediaFormat, /* drmInitData= */ null));
-            }
-            mOutputConsumer.onSeekMapFound(new SeekMap(exoplayerSeekMap));
-        }
-    }
-
-    private class TrackOutputAdapter implements TrackOutput {
-
-        private final int mTrackIndex;
-
-        private CryptoInfo mLastOutputCryptoInfo;
-        private CryptoInfo.Pattern mLastOutputEncryptionPattern;
-        private CryptoData mLastReceivedCryptoData;
-
-        @EncryptionDataReadState private int mEncryptionDataReadState;
-        private int mEncryptionDataSizeToSubtractFromSampleDataSize;
-        private int mEncryptionVectorSize;
-        private byte[] mScratchIvSpace;
-        private int mSubsampleEncryptionDataSize;
-        private int[] mScratchSubsampleEncryptedBytesCount;
-        private int[] mScratchSubsampleClearBytesCount;
-        private boolean mHasSubsampleEncryptionData;
-        private int mSkippedSupplementalDataBytes;
-
-        private TrackOutputAdapter(int trackIndex) {
-            mTrackIndex = trackIndex;
-            mScratchIvSpace = new byte[16]; // Size documented in CryptoInfo.
-            mScratchSubsampleEncryptedBytesCount = new int[32];
-            mScratchSubsampleClearBytesCount = new int[32];
-            mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
-            mLastOutputEncryptionPattern =
-                    new CryptoInfo.Pattern(/* blocksToEncrypt= */ 0, /* blocksToSkip= */ 0);
-        }
-
-        @Override
-        public void format(Format format) {
-            mTrackFormats.put(mTrackIndex, format);
-            mOutputConsumer.onTrackDataFound(
-                    mTrackIndex,
-                    new TrackData(
-                            toMediaFormat(format), toFrameworkDrmInitData(format.drmInitData)));
-        }
-
-        @Override
-        public int sampleData(
-                DataReader input,
-                int length,
-                boolean allowEndOfInput,
-                @SampleDataPart int sampleDataPart)
-                throws IOException {
-            mScratchDataReaderAdapter.setDataReader(input, length);
-            long positionBeforeReading = mScratchDataReaderAdapter.getPosition();
-            mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchDataReaderAdapter);
-            return (int) (mScratchDataReaderAdapter.getPosition() - positionBeforeReading);
-        }
-
-        @Override
-        public void sampleData(
-                ParsableByteArray data, int length, @SampleDataPart int sampleDataPart) {
-            if (sampleDataPart == SAMPLE_DATA_PART_ENCRYPTION && !mInBandCryptoInfo) {
-                while (length > 0) {
-                    switch (mEncryptionDataReadState) {
-                        case STATE_READING_SIGNAL_BYTE:
-                            int encryptionSignalByte = data.readUnsignedByte();
-                            length--;
-                            mHasSubsampleEncryptionData = ((encryptionSignalByte >> 7) & 1) != 0;
-                            mEncryptionVectorSize = encryptionSignalByte & 0x7F;
-                            mEncryptionDataSizeToSubtractFromSampleDataSize =
-                                    mEncryptionVectorSize + 1; // Signal byte.
-                            mEncryptionDataReadState = STATE_READING_INIT_VECTOR;
-                            break;
-                        case STATE_READING_INIT_VECTOR:
-                            Arrays.fill(mScratchIvSpace, (byte) 0); // Ensure 0-padding.
-                            data.readBytes(mScratchIvSpace, /* offset= */ 0, mEncryptionVectorSize);
-                            length -= mEncryptionVectorSize;
-                            if (mHasSubsampleEncryptionData) {
-                                mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE;
-                            } else {
-                                mSubsampleEncryptionDataSize = 0;
-                                mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
-                            }
-                            break;
-                        case STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE:
-                            mSubsampleEncryptionDataSize = data.readUnsignedShort();
-                            if (mScratchSubsampleClearBytesCount.length
-                                    < mSubsampleEncryptionDataSize) {
-                                mScratchSubsampleClearBytesCount =
-                                        new int[mSubsampleEncryptionDataSize];
-                                mScratchSubsampleEncryptedBytesCount =
-                                        new int[mSubsampleEncryptionDataSize];
-                            }
-                            length -= 2;
-                            mEncryptionDataSizeToSubtractFromSampleDataSize +=
-                                    2
-                                            + mSubsampleEncryptionDataSize
-                                                    * BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY;
-                            mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_DATA;
-                            break;
-                        case STATE_READING_SUBSAMPLE_ENCRYPTION_DATA:
-                            for (int i = 0; i < mSubsampleEncryptionDataSize; i++) {
-                                mScratchSubsampleClearBytesCount[i] = data.readUnsignedShort();
-                                mScratchSubsampleEncryptedBytesCount[i] = data.readInt();
-                            }
-                            length -=
-                                    mSubsampleEncryptionDataSize
-                                            * BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY;
-                            mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
-                            if (length != 0) {
-                                throw new IllegalStateException();
-                            }
-                            break;
-                        default:
-                            // Never happens.
-                            throw new IllegalStateException();
-                    }
-                }
-            } else if (sampleDataPart == SAMPLE_DATA_PART_SUPPLEMENTAL
-                    && !mIncludeSupplementalData) {
-                mSkippedSupplementalDataBytes += length;
-                data.skipBytes(length);
-            } else {
-                outputSampleData(data, length);
-            }
-        }
-
-        @Override
-        public void sampleMetadata(
-                long timeUs, int flags, int size, int offset, @Nullable CryptoData cryptoData) {
-            size -= mSkippedSupplementalDataBytes;
-            mSkippedSupplementalDataBytes = 0;
-            mOutputConsumer.onSampleCompleted(
-                    mTrackIndex,
-                    timeUs,
-                    getMediaParserFlags(flags),
-                    size - mEncryptionDataSizeToSubtractFromSampleDataSize,
-                    offset,
-                    getPopulatedCryptoInfo(cryptoData));
-            mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
-            mEncryptionDataSizeToSubtractFromSampleDataSize = 0;
-        }
-
-        @Nullable
-        private CryptoInfo getPopulatedCryptoInfo(@Nullable CryptoData cryptoData) {
-            if (cryptoData == null) {
-                // The sample is not encrypted.
-                return null;
-            } else if (mInBandCryptoInfo) {
-                if (cryptoData != mLastReceivedCryptoData) {
-                    mLastOutputCryptoInfo =
-                            createNewCryptoInfoAndPopulateWithCryptoData(cryptoData);
-                    // We are using in-band crypto info, so the IV will be ignored. But we prevent
-                    // it from being null because toString assumes it non-null.
-                    mLastOutputCryptoInfo.iv = EMPTY_BYTE_ARRAY;
-                }
-            } else /* We must populate the full CryptoInfo. */ {
-                // CryptoInfo.pattern is not accessible to the user, so the user needs to feed
-                // this CryptoInfo directly to MediaCodec. We need to create a new CryptoInfo per
-                // sample because of per-sample initialization vector changes.
-                CryptoInfo newCryptoInfo = createNewCryptoInfoAndPopulateWithCryptoData(cryptoData);
-                newCryptoInfo.iv = Arrays.copyOf(mScratchIvSpace, mScratchIvSpace.length);
-                boolean canReuseSubsampleInfo =
-                        mLastOutputCryptoInfo != null
-                                && mLastOutputCryptoInfo.numSubSamples
-                                        == mSubsampleEncryptionDataSize;
-                for (int i = 0; i < mSubsampleEncryptionDataSize && canReuseSubsampleInfo; i++) {
-                    canReuseSubsampleInfo =
-                            mLastOutputCryptoInfo.numBytesOfClearData[i]
-                                            == mScratchSubsampleClearBytesCount[i]
-                                    && mLastOutputCryptoInfo.numBytesOfEncryptedData[i]
-                                            == mScratchSubsampleEncryptedBytesCount[i];
-                }
-                newCryptoInfo.numSubSamples = mSubsampleEncryptionDataSize;
-                if (canReuseSubsampleInfo) {
-                    newCryptoInfo.numBytesOfClearData = mLastOutputCryptoInfo.numBytesOfClearData;
-                    newCryptoInfo.numBytesOfEncryptedData =
-                            mLastOutputCryptoInfo.numBytesOfEncryptedData;
-                } else {
-                    newCryptoInfo.numBytesOfClearData =
-                            Arrays.copyOf(
-                                    mScratchSubsampleClearBytesCount, mSubsampleEncryptionDataSize);
-                    newCryptoInfo.numBytesOfEncryptedData =
-                            Arrays.copyOf(
-                                    mScratchSubsampleEncryptedBytesCount,
-                                    mSubsampleEncryptionDataSize);
-                }
-                mLastOutputCryptoInfo = newCryptoInfo;
-            }
-            mLastReceivedCryptoData = cryptoData;
-            return mLastOutputCryptoInfo;
-        }
-
-        private CryptoInfo createNewCryptoInfoAndPopulateWithCryptoData(CryptoData cryptoData) {
-            CryptoInfo cryptoInfo = new CryptoInfo();
-            cryptoInfo.key = cryptoData.encryptionKey;
-            cryptoInfo.mode = cryptoData.cryptoMode;
-            if (cryptoData.clearBlocks != mLastOutputEncryptionPattern.getSkipBlocks()
-                    || cryptoData.encryptedBlocks
-                            != mLastOutputEncryptionPattern.getEncryptBlocks()) {
-                mLastOutputEncryptionPattern =
-                        new CryptoInfo.Pattern(cryptoData.encryptedBlocks, cryptoData.clearBlocks);
-            }
-            cryptoInfo.setPattern(mLastOutputEncryptionPattern);
-            return cryptoInfo;
-        }
-
-        private void outputSampleData(ParsableByteArray data, int length) {
-            mScratchParsableByteArrayAdapter.resetWithByteArray(data, length);
-            try {
-                // Read all bytes from data. ExoPlayer extractors expect all sample data to be
-                // consumed by TrackOutput implementations when passing a ParsableByteArray.
-                while (mScratchParsableByteArrayAdapter.getLength() > 0) {
-                    mOutputConsumer.onSampleDataFound(
-                            mTrackIndex, mScratchParsableByteArrayAdapter);
-                }
-            } catch (IOException e) {
-                // Unexpected.
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    private static final class DataReaderAdapter implements InputReader {
-
-        private DataReader mDataReader;
-        private int mCurrentPosition;
-        private long mLength;
-
-        public void setDataReader(DataReader dataReader, long length) {
-            mDataReader = dataReader;
-            mCurrentPosition = 0;
-            mLength = length;
-        }
-
-        // Input implementation.
-
-        @Override
-        public int read(byte[] buffer, int offset, int readLength) throws IOException {
-            int readBytes = 0;
-            readBytes = mDataReader.read(buffer, offset, readLength);
-            mCurrentPosition += readBytes;
-            return readBytes;
-        }
-
-        @Override
-        public long getPosition() {
-            return mCurrentPosition;
-        }
-
-        @Override
-        public long getLength() {
-            return mLength - mCurrentPosition;
-        }
-    }
-
-    private static final class ParsableByteArrayAdapter implements InputReader {
-
-        private ParsableByteArray mByteArray;
-        private long mLength;
-        private int mCurrentPosition;
-
-        public void resetWithByteArray(ParsableByteArray byteArray, long length) {
-            mByteArray = byteArray;
-            mCurrentPosition = 0;
-            mLength = length;
-        }
-
-        // Input implementation.
-
-        @Override
-        public int read(byte[] buffer, int offset, int readLength) {
-            mByteArray.readBytes(buffer, offset, readLength);
-            mCurrentPosition += readLength;
-            return readLength;
-        }
-
-        @Override
-        public long getPosition() {
-            return mCurrentPosition;
-        }
-
-        @Override
-        public long getLength() {
-            return mLength - mCurrentPosition;
-        }
-    }
-
-    private static final class DummyExoPlayerSeekMap
-            implements com.google.android.exoplayer2.extractor.SeekMap {
-
-        @Override
-        public boolean isSeekable() {
-            return true;
-        }
-
-        @Override
-        public long getDurationUs() {
-            return C.TIME_UNSET;
-        }
-
-        @Override
-        public SeekPoints getSeekPoints(long timeUs) {
-            com.google.android.exoplayer2.extractor.SeekPoint seekPoint =
-                    new com.google.android.exoplayer2.extractor.SeekPoint(
-                            timeUs, /* position= */ 0);
-            return new SeekPoints(seekPoint, seekPoint);
-        }
-    }
-
-    /** Creates extractor instances. */
-    private interface ExtractorFactory {
-
-        /** Returns a new extractor instance. */
-        Extractor createInstance();
-    }
-
-    // Private static methods.
-
-    private static Format toExoPlayerCaptionFormat(MediaFormat mediaFormat) {
-        Format.Builder formatBuilder =
-                new Format.Builder().setSampleMimeType(mediaFormat.getString(MediaFormat.KEY_MIME));
-        if (mediaFormat.containsKey(MediaFormat.KEY_CAPTION_SERVICE_NUMBER)) {
-            formatBuilder.setAccessibilityChannel(
-                    mediaFormat.getInteger(MediaFormat.KEY_CAPTION_SERVICE_NUMBER));
-        }
-        return formatBuilder.build();
-    }
-
-    private static MediaFormat toMediaFormat(Format format) {
-        MediaFormat result = new MediaFormat();
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_BIT_RATE, format.bitrate);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
-
-        ColorInfo colorInfo = format.colorInfo;
-        if (colorInfo != null) {
-            setOptionalMediaFormatInt(
-                    result, MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer);
-            setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_RANGE, colorInfo.colorRange);
-            setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_STANDARD, colorInfo.colorSpace);
-
-            if (format.colorInfo.hdrStaticInfo != null) {
-                result.setByteBuffer(
-                        MediaFormat.KEY_HDR_STATIC_INFO,
-                        ByteBuffer.wrap(format.colorInfo.hdrStaticInfo));
-            }
-        }
-
-        setOptionalMediaFormatString(result, MediaFormat.KEY_MIME, format.sampleMimeType);
-        setOptionalMediaFormatString(result, MediaFormat.KEY_CODECS_STRING, format.codecs);
-        if (format.frameRate != Format.NO_VALUE) {
-            result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate);
-        }
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_WIDTH, format.width);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_HEIGHT, format.height);
-
-        List<byte[]> initData = format.initializationData;
-        for (int i = 0; i < initData.size(); i++) {
-            result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i)));
-        }
-        setPcmEncoding(format, result);
-        setOptionalMediaFormatString(result, MediaFormat.KEY_LANGUAGE, format.language);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_ROTATION, format.rotationDegrees);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
-        setOptionalMediaFormatInt(
-                result, MediaFormat.KEY_CAPTION_SERVICE_NUMBER, format.accessibilityChannel);
-
-        int selectionFlags = format.selectionFlags;
-        result.setInteger(
-                MediaFormat.KEY_IS_AUTOSELECT, selectionFlags & C.SELECTION_FLAG_AUTOSELECT);
-        result.setInteger(MediaFormat.KEY_IS_DEFAULT, selectionFlags & C.SELECTION_FLAG_DEFAULT);
-        result.setInteger(
-                MediaFormat.KEY_IS_FORCED_SUBTITLE, selectionFlags & C.SELECTION_FLAG_FORCED);
-
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding);
-
-        if (format.pixelWidthHeightRatio != Format.NO_VALUE && format.pixelWidthHeightRatio != 0) {
-            int parWidth = 1;
-            int parHeight = 1;
-            if (format.pixelWidthHeightRatio < 1.0f) {
-                parHeight = 1 << 30;
-                parWidth = (int) (format.pixelWidthHeightRatio * parHeight);
-            } else if (format.pixelWidthHeightRatio > 1.0f) {
-                parWidth = 1 << 30;
-                parHeight = (int) (parWidth / format.pixelWidthHeightRatio);
-            }
-            result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, parWidth);
-            result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, parHeight);
-            result.setFloat("pixel-width-height-ratio-float", format.pixelWidthHeightRatio);
-        }
-        if (format.drmInitData != null) {
-            // The crypto mode is propagated along with sample metadata. We also include it in the
-            // format for convenient use from ExoPlayer.
-            result.setString("crypto-mode-fourcc", format.drmInitData.schemeType);
-        }
-        if (format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) {
-            result.setLong("subsample-offset-us-long", format.subsampleOffsetUs);
-        }
-        // LACK OF SUPPORT FOR:
-        //    format.id;
-        //    format.metadata;
-        //    format.stereoMode;
-        return result;
-    }
-
-    private static ByteBuffer toByteBuffer(long[] longArray) {
-        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(longArray.length * Long.BYTES);
-        for (long element : longArray) {
-            byteBuffer.putLong(element);
-        }
-        byteBuffer.flip();
-        return byteBuffer;
-    }
-
-    private static ByteBuffer toByteBuffer(int[] intArray) {
-        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(intArray.length * Integer.BYTES);
-        for (int element : intArray) {
-            byteBuffer.putInt(element);
-        }
-        byteBuffer.flip();
-        return byteBuffer;
-    }
-
-    private static String toTypeString(int type) {
-        switch (type) {
-            case C.TRACK_TYPE_VIDEO:
-                return "video";
-            case C.TRACK_TYPE_AUDIO:
-                return "audio";
-            case C.TRACK_TYPE_TEXT:
-                return "text";
-            case C.TRACK_TYPE_METADATA:
-                return "metadata";
-            default:
-                return "unknown";
-        }
-    }
-
-    private static void setPcmEncoding(Format format, MediaFormat result) {
-        int exoPcmEncoding = format.pcmEncoding;
-        setOptionalMediaFormatInt(result, "exo-pcm-encoding", format.pcmEncoding);
-        int mediaFormatPcmEncoding;
-        switch (exoPcmEncoding) {
-            case C.ENCODING_PCM_8BIT:
-                mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_8BIT;
-                break;
-            case C.ENCODING_PCM_16BIT:
-                mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_16BIT;
-                break;
-            case C.ENCODING_PCM_FLOAT:
-                mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_FLOAT;
-                break;
-            default:
-                // No matching value. Do nothing.
-                return;
-        }
-        result.setInteger(MediaFormat.KEY_PCM_ENCODING, mediaFormatPcmEncoding);
-    }
-
-    private static void setOptionalMediaFormatInt(MediaFormat mediaFormat, String key, int value) {
-        if (value != Format.NO_VALUE) {
-            mediaFormat.setInteger(key, value);
-        }
-    }
-
-    private static void setOptionalMediaFormatString(
-            MediaFormat mediaFormat, String key, @Nullable String value) {
-        if (value != null) {
-            mediaFormat.setString(key, value);
-        }
-    }
-
-    private DrmInitData toFrameworkDrmInitData(
-            com.google.android.exoplayer2.drm.DrmInitData exoDrmInitData) {
-        try {
-            return exoDrmInitData != null && mSchemeInitDataConstructor != null
-                    ? new MediaParserDrmInitData(exoDrmInitData)
-                    : null;
-        } catch (Throwable e) {
-            if (!mLoggedSchemeInitDataCreationException) {
-                mLoggedSchemeInitDataCreationException = true;
-                Log.e(TAG, "Unable to create SchemeInitData instance.");
-            }
-            return null;
-        }
-    }
-
-    /** Returns a new {@link SeekPoint} equivalent to the given {@code exoPlayerSeekPoint}. */
-    private static SeekPoint toSeekPoint(
-            com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint) {
-        return new SeekPoint(exoPlayerSeekPoint.timeUs, exoPlayerSeekPoint.position);
-    }
-
-    /**
-     * Introduces random error to the given metric value in order to prevent the identification of
-     * the parsed media.
-     */
-    private static long addDither(long value) {
-        // Generate a random in [0, 1].
-        double randomDither = ThreadLocalRandom.current().nextFloat();
-        // Clamp the random number to [0, 2 * MEDIAMETRICS_DITHER].
-        randomDither *= 2 * MEDIAMETRICS_DITHER;
-        // Translate the random number to [1 - MEDIAMETRICS_DITHER, 1 + MEDIAMETRICS_DITHER].
-        randomDither += 1 - MEDIAMETRICS_DITHER;
-        return value != -1 ? (long) (value * randomDither) : -1;
-    }
-
-    private static void assertValidNames(@NonNull String[] names) {
-        for (String name : names) {
-            if (!EXTRACTOR_FACTORIES_BY_NAME.containsKey(name)) {
-                throw new IllegalArgumentException(
-                        "Invalid extractor name: "
-                                + name
-                                + ". Supported parsers are: "
-                                + TextUtils.join(", ", EXTRACTOR_FACTORIES_BY_NAME.keySet())
-                                + ".");
-            }
-        }
-    }
-
-    private int getMediaParserFlags(int flags) {
-        @SampleFlags int result = 0;
-        result |= (flags & C.BUFFER_FLAG_ENCRYPTED) != 0 ? SAMPLE_FLAG_ENCRYPTED : 0;
-        result |= (flags & C.BUFFER_FLAG_KEY_FRAME) != 0 ? SAMPLE_FLAG_KEY_FRAME : 0;
-        result |= (flags & C.BUFFER_FLAG_DECODE_ONLY) != 0 ? SAMPLE_FLAG_DECODE_ONLY : 0;
-        result |=
-                (flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0 && mIncludeSupplementalData
-                        ? SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA
-                        : 0;
-        result |= (flags & C.BUFFER_FLAG_LAST_SAMPLE) != 0 ? SAMPLE_FLAG_LAST_SAMPLE : 0;
-        return result;
-    }
-
-    @Nullable
-    private static Constructor<DrmInitData.SchemeInitData> getSchemeInitDataConstructor() {
-        // TODO: Use constructor statically when available.
-        Constructor<DrmInitData.SchemeInitData> constructor;
-        try {
-            return DrmInitData.SchemeInitData.class.getConstructor(
-                    UUID.class, String.class, byte[].class);
-        } catch (Throwable e) {
-            Log.e(TAG, "Unable to get SchemeInitData constructor.");
-            return null;
-        }
-    }
-
-    // Native methods.
-
-    private native void nativeSubmitMetrics(
-            String logSessionId,
-            String parserName,
-            boolean createdByName,
-            String parserPool,
-            String lastObservedExceptionName,
-            long resourceByteCount,
-            long durationMillis,
-            String trackMimeTypes,
-            String trackCodecs,
-            String alteredParameters,
-            int videoWidth,
-            int videoHeight);
-
-    // Static initialization.
-
-    static {
-        System.loadLibrary(JNI_LIBRARY_NAME);
-
-        // Using a LinkedHashMap to keep the insertion order when iterating over the keys.
-        LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>();
-        // Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering,
-        // which in turn aims to minimize the chances of incorrect extractor selections.
-        extractorFactoriesByName.put(PARSER_NAME_MATROSKA, MatroskaExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_FMP4, FragmentedMp4Extractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_MP4, Mp4Extractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_MP3, Mp3Extractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_ADTS, AdtsExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_AC3, Ac3Extractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_TS, TsExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_FLV, FlvExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_OGG, OggExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_PS, PsExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_WAV, WavExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_AMR, AmrExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_AC4, Ac4Extractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_FLAC, FlacExtractor::new);
-        EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName);
-
-        HashMap<String, Class> expectedTypeByParameterName = new HashMap<>();
-        expectedTypeByParameterName.put(PARAMETER_ADTS_ENABLE_CBR_SEEKING, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_AMR_ENABLE_CBR_SEEKING, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_FLAC_DISABLE_ID3, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_EDIT_LISTS, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_TFDT_BOX, Boolean.class);
-        expectedTypeByParameterName.put(
-                PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MP3_DISABLE_ID3, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_CBR_SEEKING, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_INDEX_SEEKING, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_MODE, String.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AAC_STREAM, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AVC_STREAM, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_DETECT_ACCESS_UNITS, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_IN_BAND_CRYPTO_INFO, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_INCLUDE_SUPPLEMENTAL_DATA, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_IGNORE_TIMESTAMP_OFFSET, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_EAGERLY_EXPOSE_TRACKTYPE, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_EXPOSE_DUMMY_SEEKMAP, Boolean.class);
-        expectedTypeByParameterName.put(
-                PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT, Boolean.class);
-        expectedTypeByParameterName.put(
-                PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_EXPOSE_EMSG_TRACK, Boolean.class);
-        // We do not check PARAMETER_EXPOSE_CAPTION_FORMATS here, and we do it in setParameters
-        // instead. Checking that the value is a List is insufficient to catch wrong parameter
-        // value types.
-        int sumOfParameterNameLengths =
-                expectedTypeByParameterName.keySet().stream()
-                        .map(String::length)
-                        .reduce(0, Integer::sum);
-        sumOfParameterNameLengths += PARAMETER_EXPOSE_CAPTION_FORMATS.length();
-        // Add space for any required separators.
-        MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH =
-                sumOfParameterNameLengths + expectedTypeByParameterName.size();
-
-        EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName);
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java
deleted file mode 100644
index 7d07eb3..0000000
--- a/apex/media/framework/java/android/media/MediaSession2.java
+++ /dev/null
@@ -1,932 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
-import static android.media.MediaConstants.KEY_CONNECTION_HINTS;
-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_SESSION2LINK;
-import static android.media.MediaConstants.KEY_TOKEN_EXTRAS;
-import static android.media.Session2Command.Result.RESULT_ERROR_UNKNOWN_ERROR;
-import static android.media.Session2Command.Result.RESULT_INFO_SKIPPED;
-import static android.media.Session2Token.TYPE_SESSION;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.media.session.MediaSessionManager;
-import android.media.session.MediaSessionManager.RemoteUserInfo;
-import android.os.BadParcelableException;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.Process;
-import android.os.ResultReceiver;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.modules.utils.build.SdkLevel;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- * <p>
- * Allows a media app to expose its transport controls and playback information in a process to
- * other processes including the Android framework and other apps.
- */
-public class MediaSession2 implements AutoCloseable {
-    static final String TAG = "MediaSession2";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    // Note: This checks the uniqueness of a session ID only in a single process.
-    // When the framework becomes able to check the uniqueness, this logic should be removed.
-    //@GuardedBy("MediaSession.class")
-    private static final List<String> SESSION_ID_LIST = new ArrayList<>();
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Object mLock = new Object();
-    //@GuardedBy("mLock")
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Map<Controller2Link, ControllerInfo> mConnectedControllers = new HashMap<>();
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Context mContext;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Executor mCallbackExecutor;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final SessionCallback mCallback;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Session2Link mSessionStub;
-
-    private final String mSessionId;
-    private final PendingIntent mSessionActivity;
-    private final Session2Token mSessionToken;
-    private final MediaSessionManager mMediaSessionManager;
-    private final MediaCommunicationManager mCommunicationManager;
-    private final Handler mResultHandler;
-
-    //@GuardedBy("mLock")
-    private boolean mClosed;
-    //@GuardedBy("mLock")
-    private boolean mPlaybackActive;
-    //@GuardedBy("mLock")
-    private ForegroundServiceEventCallback mForegroundServiceEventCallback;
-
-    MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity,
-            @NonNull Executor callbackExecutor, @NonNull SessionCallback callback,
-            @NonNull Bundle tokenExtras) {
-        synchronized (MediaSession2.class) {
-            if (SESSION_ID_LIST.contains(id)) {
-                throw new IllegalStateException("Session ID must be unique. ID=" + id);
-            }
-            SESSION_ID_LIST.add(id);
-        }
-
-        mContext = context;
-        mSessionId = id;
-        mSessionActivity = sessionActivity;
-        mCallbackExecutor = callbackExecutor;
-        mCallback = callback;
-        mSessionStub = new Session2Link(this);
-        mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(),
-                mSessionStub, tokenExtras);
-        if (SdkLevel.isAtLeastS()) {
-            mCommunicationManager = mContext.getSystemService(MediaCommunicationManager.class);
-            mMediaSessionManager = null;
-        } else {
-            mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
-            mCommunicationManager = null;
-        }
-        // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
-        mResultHandler = new Handler(context.getMainLooper());
-        mClosed = false;
-    }
-
-    @Override
-    public void close() {
-        try {
-            List<ControllerInfo> controllerInfos;
-            ForegroundServiceEventCallback callback;
-            synchronized (mLock) {
-                if (mClosed) {
-                    return;
-                }
-                mClosed = true;
-                controllerInfos = getConnectedControllers();
-                mConnectedControllers.clear();
-                callback = mForegroundServiceEventCallback;
-                mForegroundServiceEventCallback = null;
-            }
-            synchronized (MediaSession2.class) {
-                SESSION_ID_LIST.remove(mSessionId);
-            }
-            if (callback != null) {
-                callback.onSessionClosed(this);
-            }
-            for (ControllerInfo info : controllerInfos) {
-                info.notifyDisconnected();
-            }
-        } catch (Exception e) {
-            // Should not be here.
-        }
-    }
-
-    /**
-     * Returns the session ID
-     */
-    @NonNull
-    public String getId() {
-        return mSessionId;
-    }
-
-    /**
-     * Returns the {@link Session2Token} for creating {@link MediaController2}.
-     */
-    @NonNull
-    public Session2Token getToken() {
-        return mSessionToken;
-    }
-
-    /**
-     * Broadcasts a session command to all the connected controllers
-     * <p>
-     * @param command the session command
-     * @param args optional arguments
-     */
-    public void broadcastSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
-        if (command == null) {
-            throw new IllegalArgumentException("command shouldn't be null");
-        }
-        List<ControllerInfo> controllerInfos = getConnectedControllers();
-        for (ControllerInfo controller : controllerInfos) {
-            controller.sendSessionCommand(command, args, null);
-        }
-    }
-
-    /**
-     * Sends a session command to a specific controller
-     * <p>
-     * @param controller the controller to get the session command
-     * @param command the session command
-     * @param args optional arguments
-     * @return a token which will be sent together in {@link SessionCallback#onCommandResult}
-     *     when its result is received.
-     */
-    @NonNull
-    public Object sendSessionCommand(@NonNull ControllerInfo controller,
-            @NonNull Session2Command command, @Nullable Bundle args) {
-        if (controller == null) {
-            throw new IllegalArgumentException("controller shouldn't be null");
-        }
-        if (command == null) {
-            throw new IllegalArgumentException("command shouldn't be null");
-        }
-        ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) {
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                controller.receiveCommandResult(this);
-                mCallbackExecutor.execute(() -> {
-                    mCallback.onCommandResult(MediaSession2.this, controller, this,
-                            command, new Session2Command.Result(resultCode, resultData));
-                });
-            }
-        };
-        controller.sendSessionCommand(command, args, resultReceiver);
-        return resultReceiver;
-    }
-
-    /**
-     * Cancels the session command previously sent.
-     *
-     * @param controller the controller to get the session command
-     * @param token the token which is returned from {@link #sendSessionCommand}.
-     */
-    public void cancelSessionCommand(@NonNull ControllerInfo controller, @NonNull Object token) {
-        if (controller == null) {
-            throw new IllegalArgumentException("controller shouldn't be null");
-        }
-        if (token == null) {
-            throw new IllegalArgumentException("token shouldn't be null");
-        }
-        controller.cancelSessionCommand(token);
-    }
-
-    /**
-     * Sets whether the playback is active (i.e. playing something)
-     *
-     * @param playbackActive {@code true} if the playback active, {@code false} otherwise.
-     **/
-    public void setPlaybackActive(boolean playbackActive) {
-        final ForegroundServiceEventCallback serviceCallback;
-        synchronized (mLock) {
-            if (mPlaybackActive == playbackActive) {
-                return;
-            }
-            mPlaybackActive = playbackActive;
-            serviceCallback = mForegroundServiceEventCallback;
-        }
-        if (serviceCallback != null) {
-            serviceCallback.onPlaybackActiveChanged(this, playbackActive);
-        }
-        List<ControllerInfo> controllerInfos = getConnectedControllers();
-        for (ControllerInfo controller : controllerInfos) {
-            controller.notifyPlaybackActiveChanged(playbackActive);
-        }
-    }
-
-    /**
-     * Returns whether the playback is active (i.e. playing something)
-     *
-     * @return {@code true} if the playback active, {@code false} otherwise.
-     */
-    public boolean isPlaybackActive() {
-        synchronized (mLock) {
-            return mPlaybackActive;
-        }
-    }
-
-    /**
-     * Gets the list of the connected controllers
-     *
-     * @return list of the connected controllers.
-     */
-    @NonNull
-    public List<ControllerInfo> getConnectedControllers() {
-        List<ControllerInfo> controllers = new ArrayList<>();
-        synchronized (mLock) {
-            controllers.addAll(mConnectedControllers.values());
-        }
-        return controllers;
-    }
-
-    /**
-     * Returns whether the given bundle includes non-framework Parcelables.
-     */
-    static boolean hasCustomParcelable(@Nullable Bundle bundle) {
-        if (bundle == null) {
-            return false;
-        }
-
-        // Try writing the bundle to parcel, and read it with framework classloader.
-        Parcel parcel = null;
-        try {
-            parcel = Parcel.obtain();
-            parcel.writeBundle(bundle);
-            parcel.setDataPosition(0);
-            Bundle out = parcel.readBundle(null);
-
-            for (String key : out.keySet()) {
-                out.get(key);
-            }
-        } catch (BadParcelableException e) {
-            Log.d(TAG, "Custom parcelable in bundle.", e);
-            return true;
-        } finally {
-            if (parcel != null) {
-                parcel.recycle();
-            }
-        }
-        return false;
-    }
-
-    boolean isClosed() {
-        synchronized (mLock) {
-            return mClosed;
-        }
-    }
-
-    SessionCallback getCallback() {
-        return mCallback;
-    }
-
-    boolean isTrustedForMediaControl(RemoteUserInfo remoteUserInfo) {
-        if (SdkLevel.isAtLeastS()) {
-            return mCommunicationManager.isTrustedForMediaControl(remoteUserInfo);
-        } else {
-            return mMediaSessionManager.isTrustedForMediaControl(remoteUserInfo);
-        }
-    }
-
-    void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) {
-        synchronized (mLock) {
-            if (mForegroundServiceEventCallback == callback) {
-                return;
-            }
-            if (mForegroundServiceEventCallback != null && callback != null) {
-                throw new IllegalStateException("A session cannot be added to multiple services");
-            }
-            mForegroundServiceEventCallback = callback;
-        }
-    }
-
-    // Called by Session2Link.onConnect and MediaSession2Service.MediaSession2ServiceStub.connect
-    void onConnect(final Controller2Link controller, int callingPid, int callingUid, int seq,
-            Bundle connectionRequest) {
-        if (callingPid == 0) {
-            // The pid here is from Binder.getCallingPid(), which can be 0 for an oneway call from
-            // the remote process. If it's the case, use PID from the connectionRequest.
-            callingPid = connectionRequest.getInt(KEY_PID);
-        }
-        String callingPkg = connectionRequest.getString(KEY_PACKAGE_NAME);
-
-        RemoteUserInfo remoteUserInfo = new RemoteUserInfo(callingPkg, callingPid, callingUid);
-
-        Bundle connectionHints = connectionRequest.getBundle(KEY_CONNECTION_HINTS);
-        if (connectionHints == null) {
-            Log.w(TAG, "connectionHints shouldn't be null.");
-            connectionHints = Bundle.EMPTY;
-        } else if (hasCustomParcelable(connectionHints)) {
-            Log.w(TAG, "connectionHints contain custom parcelable. Ignoring.");
-            connectionHints = Bundle.EMPTY;
-        }
-
-        final ControllerInfo controllerInfo = new ControllerInfo(
-                remoteUserInfo,
-                isTrustedForMediaControl(remoteUserInfo),
-                controller,
-                connectionHints);
-        mCallbackExecutor.execute(() -> {
-            boolean connected = false;
-            try {
-                if (isClosed()) {
-                    return;
-                }
-                controllerInfo.mAllowedCommands =
-                        mCallback.onConnect(MediaSession2.this, controllerInfo);
-                // Don't reject connection for the request from trusted app.
-                // Otherwise server will fail to retrieve session's information to dispatch
-                // media keys to.
-                if (controllerInfo.mAllowedCommands == null && !controllerInfo.isTrusted()) {
-                    return;
-                }
-                if (controllerInfo.mAllowedCommands == null) {
-                    // For trusted apps, send non-null allowed commands to keep
-                    // connection.
-                    controllerInfo.mAllowedCommands =
-                            new Session2CommandGroup.Builder().build();
-                }
-                if (DEBUG) {
-                    Log.d(TAG, "Accepting connection: " + controllerInfo);
-                }
-                // If connection is accepted, notify the current state to the controller.
-                // It's needed because we cannot call synchronous calls between
-                // session/controller.
-                Bundle connectionResult = new Bundle();
-                connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub);
-                connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
-                        controllerInfo.mAllowedCommands);
-                connectionResult.putBoolean(KEY_PLAYBACK_ACTIVE, isPlaybackActive());
-                connectionResult.putBundle(KEY_TOKEN_EXTRAS, mSessionToken.getExtras());
-
-                // Double check if session is still there, because close() can be called in
-                // another thread.
-                if (isClosed()) {
-                    return;
-                }
-                controllerInfo.notifyConnected(connectionResult);
-                synchronized (mLock) {
-                    if (mConnectedControllers.containsKey(controller)) {
-                        Log.w(TAG, "Controller " + controllerInfo + " has sent connection"
-                                + " request multiple times");
-                    }
-                    mConnectedControllers.put(controller, controllerInfo);
-                }
-                mCallback.onPostConnect(MediaSession2.this, controllerInfo);
-                connected = true;
-            } finally {
-                if (!connected || isClosed()) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Rejecting connection or notifying that session is closed"
-                                + ", controllerInfo=" + controllerInfo);
-                    }
-                    synchronized (mLock) {
-                        mConnectedControllers.remove(controller);
-                    }
-                    controllerInfo.notifyDisconnected();
-                }
-            }
-        });
-    }
-
-    // Called by Session2Link.onDisconnect
-    void onDisconnect(@NonNull final Controller2Link controller, int seq) {
-        final ControllerInfo controllerInfo;
-        synchronized (mLock) {
-            controllerInfo = mConnectedControllers.remove(controller);
-        }
-        if (controllerInfo == null) {
-            return;
-        }
-        mCallbackExecutor.execute(() -> {
-            mCallback.onDisconnected(MediaSession2.this, controllerInfo);
-        });
-    }
-
-    // Called by Session2Link.onSessionCommand
-    void onSessionCommand(@NonNull final Controller2Link controller, final int seq,
-            final Session2Command command, final Bundle args,
-            @Nullable ResultReceiver resultReceiver) {
-        if (controller == null) {
-            return;
-        }
-        final ControllerInfo controllerInfo;
-        synchronized (mLock) {
-            controllerInfo = mConnectedControllers.get(controller);
-        }
-        if (controllerInfo == null) {
-            return;
-        }
-
-        // TODO: check allowed commands.
-        synchronized (mLock) {
-            controllerInfo.addRequestedCommandSeqNumber(seq);
-        }
-        mCallbackExecutor.execute(() -> {
-            if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) {
-                if (resultReceiver != null) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                }
-                return;
-            }
-            Session2Command.Result result = mCallback.onSessionCommand(
-                    MediaSession2.this, controllerInfo, command, args);
-            if (resultReceiver != null) {
-                if (result == null) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                } else {
-                    resultReceiver.send(result.getResultCode(), result.getResultData());
-                }
-            }
-        });
-    }
-
-    // Called by Session2Link.onCancelCommand
-    void onCancelCommand(@NonNull final Controller2Link controller, final int seq) {
-        final ControllerInfo controllerInfo;
-        synchronized (mLock) {
-            controllerInfo = mConnectedControllers.get(controller);
-        }
-        if (controllerInfo == null) {
-            return;
-        }
-        controllerInfo.removeRequestedCommandSeqNumber(seq);
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Builder for {@link MediaSession2}.
-     * <p>
-     * Any incoming event from the {@link MediaController2} will be handled on the callback
-     * executor. If it's not set, {@link Context#getMainExecutor()} will be used by default.
-     */
-    public static final class Builder {
-        private Context mContext;
-        private String mId;
-        private PendingIntent mSessionActivity;
-        private Executor mCallbackExecutor;
-        private SessionCallback mCallback;
-        private Bundle mExtras;
-
-        /**
-         * Creates a builder for {@link MediaSession2}.
-         *
-         * @param context Context
-         * @throws IllegalArgumentException if context is {@code null}.
-         */
-        public Builder(@NonNull Context context) {
-            if (context == null) {
-                throw new IllegalArgumentException("context shouldn't be null");
-            }
-            mContext = context;
-        }
-
-        /**
-         * Set an intent for launching UI for this Session. This can be used as a
-         * quick link to an ongoing media screen. The intent should be for an
-         * activity that may be started using {@link Context#startActivity(Intent)}.
-         *
-         * @param pi The intent to launch to show UI for this session.
-         * @return The Builder to allow chaining
-         */
-        @NonNull
-        public Builder setSessionActivity(@Nullable PendingIntent pi) {
-            mSessionActivity = pi;
-            return this;
-        }
-
-        /**
-         * Set ID of the session. If it's not set, an empty string will be used to create a session.
-         * <p>
-         * Use this if and only if your app supports multiple playback at the same time and also
-         * wants to provide external apps to have finer controls of them.
-         *
-         * @param id id of the session. Must be unique per package.
-         * @throws IllegalArgumentException if id is {@code null}.
-         * @return The Builder to allow chaining
-         */
-        @NonNull
-        public Builder setId(@NonNull String id) {
-            if (id == null) {
-                throw new IllegalArgumentException("id shouldn't be null");
-            }
-            mId = id;
-            return this;
-        }
-
-        /**
-         * Set callback for the session and its executor.
-         *
-         * @param executor callback executor
-         * @param callback session callback.
-         * @return The Builder to allow chaining
-         */
-        @NonNull
-        public Builder setSessionCallback(@NonNull Executor executor,
-                @NonNull SessionCallback callback) {
-            mCallbackExecutor = executor;
-            mCallback = callback;
-            return this;
-        }
-
-        /**
-         * Set extras for the session token. If null or not set, {@link Session2Token#getExtras()}
-         * will return an empty {@link Bundle}. An {@link IllegalArgumentException} will be thrown
-         * if the bundle contains any non-framework Parcelable objects.
-         *
-         * @return The Builder to allow chaining
-         * @see Session2Token#getExtras()
-         */
-        @NonNull
-        public Builder setExtras(@NonNull Bundle extras) {
-            if (extras == null) {
-                throw new NullPointerException("extras shouldn't be null");
-            }
-            if (hasCustomParcelable(extras)) {
-                throw new IllegalArgumentException(
-                        "extras shouldn't contain any custom parcelables");
-            }
-            mExtras = new Bundle(extras);
-            return this;
-        }
-
-        /**
-         * Build {@link MediaSession2}.
-         *
-         * @return a new session
-         * @throws IllegalStateException if the session with the same id is already exists for the
-         *      package.
-         */
-        @NonNull
-        public MediaSession2 build() {
-            if (mCallbackExecutor == null) {
-                mCallbackExecutor = mContext.getMainExecutor();
-            }
-            if (mCallback == null) {
-                mCallback = new SessionCallback() {};
-            }
-            if (mId == null) {
-                mId = "";
-            }
-            if (mExtras == null) {
-                mExtras = Bundle.EMPTY;
-            }
-            MediaSession2 session2 = new MediaSession2(mContext, mId, mSessionActivity,
-                    mCallbackExecutor, mCallback, mExtras);
-
-            // Notify framework about the newly create session after the constructor is finished.
-            // Otherwise, framework may access the session before the initialization is finished.
-            try {
-                if (SdkLevel.isAtLeastS()) {
-                    MediaCommunicationManager manager =
-                            mContext.getSystemService(MediaCommunicationManager.class);
-                    manager.notifySession2Created(session2.getToken());
-                } else {
-                    MediaSessionManager manager =
-                            mContext.getSystemService(MediaSessionManager.class);
-                    manager.notifySession2Created(session2.getToken());
-                }
-            } catch (Exception e) {
-                session2.close();
-                throw e;
-            }
-
-            return session2;
-        }
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Information of a controller.
-     */
-    public static final class ControllerInfo {
-        private final RemoteUserInfo mRemoteUserInfo;
-        private final boolean mIsTrusted;
-        private final Controller2Link mControllerBinder;
-        private final Bundle mConnectionHints;
-        private final Object mLock = new Object();
-        //@GuardedBy("mLock")
-        private int mNextSeqNumber;
-        //@GuardedBy("mLock")
-        private ArrayMap<ResultReceiver, Integer> mPendingCommands;
-        //@GuardedBy("mLock")
-        private ArraySet<Integer> mRequestedCommandSeqNumbers;
-
-        @SuppressWarnings("WeakerAccess") /* synthetic access */
-        Session2CommandGroup mAllowedCommands;
-
-        /**
-         * @param remoteUserInfo remote user info
-         * @param trusted {@code true} if trusted, {@code false} otherwise
-         * @param controllerBinder Controller2Link for the connected controller.
-         * @param connectionHints a session-specific argument sent from the controller for the
-         *                        connection. The contents of this bundle may affect the
-         *                        connection result.
-         */
-        ControllerInfo(@NonNull RemoteUserInfo remoteUserInfo, boolean trusted,
-                @Nullable Controller2Link controllerBinder, @NonNull Bundle connectionHints) {
-            mRemoteUserInfo = remoteUserInfo;
-            mIsTrusted = trusted;
-            mControllerBinder = controllerBinder;
-            mConnectionHints = connectionHints;
-            mPendingCommands = new ArrayMap<>();
-            mRequestedCommandSeqNumbers = new ArraySet<>();
-        }
-
-        /**
-         * @return remote user info of the controller.
-         */
-        @NonNull
-        public RemoteUserInfo getRemoteUserInfo() {
-            return mRemoteUserInfo;
-        }
-
-        /**
-         * @return package name of the controller.
-         */
-        @NonNull
-        public String getPackageName() {
-            return mRemoteUserInfo.getPackageName();
-        }
-
-        /**
-         * @return uid of the controller. Can be a negative value if the uid cannot be obtained.
-         */
-        public int getUid() {
-            return mRemoteUserInfo.getUid();
-        }
-
-        /**
-         * @return connection hints sent from controller.
-         */
-        @NonNull
-        public Bundle getConnectionHints() {
-            return new Bundle(mConnectionHints);
-        }
-
-        /**
-         * Return if the controller has granted {@code android.permission.MEDIA_CONTENT_CONTROL} or
-         * has a enabled notification listener so can be trusted to accept connection and incoming
-         * command request.
-         *
-         * @return {@code true} if the controller is trusted.
-         * @hide
-         */
-        public boolean isTrusted() {
-            return mIsTrusted;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mControllerBinder, mRemoteUserInfo);
-        }
-
-        @Override
-        public boolean equals(@Nullable Object obj) {
-            if (!(obj instanceof ControllerInfo)) return false;
-            if (this == obj) return true;
-
-            ControllerInfo other = (ControllerInfo) obj;
-            if (mControllerBinder != null || other.mControllerBinder != null) {
-                return Objects.equals(mControllerBinder, other.mControllerBinder);
-            }
-            return mRemoteUserInfo.equals(other.mRemoteUserInfo);
-        }
-
-        @Override
-        @NonNull
-        public String toString() {
-            return "ControllerInfo {pkg=" + mRemoteUserInfo.getPackageName() + ", uid="
-                    + mRemoteUserInfo.getUid() + ", allowedCommands=" + mAllowedCommands + "})";
-        }
-
-        void notifyConnected(Bundle connectionResult) {
-            if (mControllerBinder == null) return;
-
-            try {
-                mControllerBinder.notifyConnected(getNextSeqNumber(), connectionResult);
-            } catch (RuntimeException e) {
-                // Controller may be died prematurely.
-            }
-        }
-
-        void notifyDisconnected() {
-            if (mControllerBinder == null) return;
-
-            try {
-                mControllerBinder.notifyDisconnected(getNextSeqNumber());
-            } catch (RuntimeException e) {
-                // Controller may be died prematurely.
-            }
-        }
-
-        void notifyPlaybackActiveChanged(boolean playbackActive) {
-            if (mControllerBinder == null) return;
-
-            try {
-                mControllerBinder.notifyPlaybackActiveChanged(getNextSeqNumber(), playbackActive);
-            } catch (RuntimeException e) {
-                // Controller may be died prematurely.
-            }
-        }
-
-        void sendSessionCommand(Session2Command command, Bundle args,
-                ResultReceiver resultReceiver) {
-            if (mControllerBinder == null) return;
-
-            try {
-                int seq = getNextSeqNumber();
-                synchronized (mLock) {
-                    mPendingCommands.put(resultReceiver, seq);
-                }
-                mControllerBinder.sendSessionCommand(seq, command, args, resultReceiver);
-            } catch (RuntimeException e) {
-                // Controller may be died prematurely.
-                synchronized (mLock) {
-                    mPendingCommands.remove(resultReceiver);
-                }
-                resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null);
-            }
-        }
-
-        void cancelSessionCommand(@NonNull Object token) {
-            if (mControllerBinder == null) return;
-            Integer seq;
-            synchronized (mLock) {
-                seq = mPendingCommands.remove(token);
-            }
-            if (seq != null) {
-                mControllerBinder.cancelSessionCommand(seq);
-            }
-        }
-
-        void receiveCommandResult(ResultReceiver resultReceiver) {
-            synchronized (mLock) {
-                mPendingCommands.remove(resultReceiver);
-            }
-        }
-
-        void addRequestedCommandSeqNumber(int seq) {
-            synchronized (mLock) {
-                mRequestedCommandSeqNumbers.add(seq);
-            }
-        }
-
-        boolean removeRequestedCommandSeqNumber(int seq) {
-            synchronized (mLock) {
-                return mRequestedCommandSeqNumbers.remove(seq);
-            }
-        }
-
-        private int getNextSeqNumber() {
-            synchronized (mLock) {
-                return mNextSeqNumber++;
-            }
-        }
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Callback to be called for all incoming commands from {@link MediaController2}s.
-     */
-    public abstract static class SessionCallback {
-        /**
-         * Called when a controller is created for this session. Return allowed commands for
-         * controller. By default it returns {@code null}.
-         * <p>
-         * You can reject the connection by returning {@code null}. In that case, controller
-         * receives {@link MediaController2.ControllerCallback#onDisconnected(MediaController2)}
-         * and cannot be used.
-         * <p>
-         * The controller hasn't connected yet in this method, so calls to the controller
-         * (e.g. {@link #sendSessionCommand}) would be ignored. Override {@link #onPostConnect} for
-         * the custom initialization for the controller instead.
-         *
-         * @param session the session for this event
-         * @param controller controller information.
-         * @return allowed commands. Can be {@code null} to reject connection.
-         */
-        @Nullable
-        public Session2CommandGroup onConnect(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller) {
-            return null;
-        }
-
-        /**
-         * Called immediately after a controller is connected. This is a convenient method to add
-         * custom initialization between the session and a controller.
-         * <p>
-         * Note that calls to the controller (e.g. {@link #sendSessionCommand}) work here but don't
-         * work in {@link #onConnect} because the controller hasn't connected yet in
-         * {@link #onConnect}.
-         *
-         * @param session the session for this event
-         * @param controller controller information.
-         */
-        public void onPostConnect(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller) {
-        }
-
-        /**
-         * Called when a controller is disconnected
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         */
-        public void onDisconnected(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller) {}
-
-        /**
-         * Called when a controller sent a session command.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param command the session command
-         * @param args optional arguments
-         * @return the result for the session command. If {@code null}, RESULT_INFO_SKIPPED
-         *         will be sent to the session.
-         */
-        @Nullable
-        public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull Session2Command command,
-                @Nullable Bundle args) {
-            return null;
-        }
-
-        /**
-         * Called when the command sent to the controller is finished.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param token the token got from {@link MediaSession2#sendSessionCommand}
-         * @param command the session command
-         * @param result the result of the session command
-         */
-        public void onCommandResult(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull Object token,
-                @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
-    }
-
-    abstract static class ForegroundServiceEventCallback {
-        public void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {}
-        public void onSessionClosed(MediaSession2 session) {}
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaSession2Service.java b/apex/media/framework/java/android/media/MediaSession2Service.java
deleted file mode 100644
index 9f80c43..0000000
--- a/apex/media/framework/java/android/media/MediaSession2Service.java
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright 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.media;
-
-import static android.media.MediaConstants.KEY_CONNECTION_HINTS;
-import static android.media.MediaConstants.KEY_PACKAGE_NAME;
-import static android.media.MediaConstants.KEY_PID;
-
-import android.annotation.CallSuper;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.session.MediaSessionManager;
-import android.media.session.MediaSessionManager.RemoteUserInfo;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- * <p>
- * Service containing {@link MediaSession2}.
- */
-public abstract class MediaSession2Service extends Service {
-    /**
-     * The {@link Intent} that must be declared as handled by the service.
-     */
-    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);
-
-    private final MediaSession2.ForegroundServiceEventCallback mForegroundServiceEventCallback =
-            new MediaSession2.ForegroundServiceEventCallback() {
-                @Override
-                public void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {
-                    MediaSession2Service.this.onPlaybackActiveChanged(session, playbackActive);
-                }
-
-                @Override
-                public void onSessionClosed(MediaSession2 session) {
-                    removeSession(session);
-                }
-            };
-
-    private final Object mLock = new Object();
-    //@GuardedBy("mLock")
-    private NotificationManager mNotificationManager;
-    //@GuardedBy("mLock")
-    private MediaSessionManager mMediaSessionManager;
-    //@GuardedBy("mLock")
-    private Intent mStartSelfIntent;
-    //@GuardedBy("mLock")
-    private Map<String, MediaSession2> mSessions = new ArrayMap<>();
-    //@GuardedBy("mLock")
-    private Map<MediaSession2, MediaNotification> mNotifications = new ArrayMap<>();
-    //@GuardedBy("mLock")
-    private MediaSession2ServiceStub mStub;
-
-    /**
-     * Called by the system when the service is first created. Do not call this method directly.
-     * <p>
-     * Override this method if you need your own initialization. Derived classes MUST call through
-     * to the super class's implementation of this method.
-     */
-    @CallSuper
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        synchronized (mLock) {
-            mStub = new MediaSession2ServiceStub(this);
-            mStartSelfIntent = new Intent(this, this.getClass());
-            mNotificationManager =
-                    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-            mMediaSessionManager =
-                    (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
-        }
-    }
-
-    @CallSuper
-    @Override
-    @Nullable
-    public IBinder onBind(@NonNull Intent intent) {
-        if (SERVICE_INTERFACE.equals(intent.getAction())) {
-            synchronized (mLock) {
-                return mStub;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Called by the system to notify that it is no longer used and is being removed. Do not call
-     * this method directly.
-     * <p>
-     * Override this method if you need your own clean up. Derived classes MUST call through
-     * to the super class's implementation of this method.
-     */
-    @CallSuper
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        synchronized (mLock) {
-            List<MediaSession2> sessions = getSessions();
-            for (MediaSession2 session : sessions) {
-                removeSession(session);
-            }
-            mSessions.clear();
-            mNotifications.clear();
-        }
-        mStub.close();
-    }
-
-    /**
-     * Called when a {@link MediaController2} is created with the this service's
-     * {@link Session2Token}. Return the session for telling the controller which session to
-     * connect. Return {@code null} to reject the connection from this controller.
-     * <p>
-     * Session returned here will be added to this service automatically. You don't need to call
-     * {@link #addSession(MediaSession2)} for that.
-     * <p>
-     * This method is always called on the main thread.
-     *
-     * @param controllerInfo information of the controller which is trying to connect.
-     * @return a {@link MediaSession2} instance for the controller to connect to, or {@code null}
-     *         to reject connection
-     * @see MediaSession2.Builder
-     * @see #getSessions()
-     */
-    @Nullable
-    public abstract MediaSession2 onGetSession(@NonNull ControllerInfo controllerInfo);
-
-    /**
-     * Called to update the media notification when the playback state changes.
-     * <p>
-     * If playback is active and a notification is returned, the service uses it to become a
-     * foreground service. If playback is not active then the notification is still posted, but the
-     * service does not become a foreground service.
-     * <p>
-     * Apps must request the {@link android.Manifest.permission#FOREGROUND_SERVICE} permission
-     * in order to use this API. For apps targeting {@link android.os.Build.VERSION_CODES#TIRAMISU}
-     * or later, notifications will only be posted if the app has also been granted the
-     * {@link android.Manifest.permission#POST_NOTIFICATIONS} permission.
-     *
-     * @param session the session for which an updated media notification is required.
-     * @return the {@link MediaNotification}. Can be {@code null}.
-     */
-    @Nullable
-    public abstract MediaNotification onUpdateNotification(@NonNull MediaSession2 session);
-
-    /**
-     * Adds a session to this service.
-     * <p>
-     * Added session will be removed automatically when it's closed, or removed when
-     * {@link #removeSession} is called.
-     *
-     * @param session a session to be added.
-     * @see #removeSession(MediaSession2)
-     */
-    public final void addSession(@NonNull MediaSession2 session) {
-        if (session == null) {
-            throw new IllegalArgumentException("session shouldn't be null");
-        }
-        if (session.isClosed()) {
-            throw new IllegalArgumentException("session is already closed");
-        }
-        synchronized (mLock) {
-            MediaSession2 previousSession = mSessions.get(session.getId());
-            if (previousSession != null) {
-                if (previousSession != session) {
-                    Log.w(TAG, "Session ID should be unique, ID=" + session.getId()
-                            + ", previous=" + previousSession + ", session=" + session);
-                }
-                return;
-            }
-            mSessions.put(session.getId(), session);
-            session.setForegroundServiceEventCallback(mForegroundServiceEventCallback);
-        }
-    }
-
-    /**
-     * Removes a session from this service.
-     *
-     * @param session a session to be removed.
-     * @see #addSession(MediaSession2)
-     */
-    public final void removeSession(@NonNull MediaSession2 session) {
-        if (session == null) {
-            throw new IllegalArgumentException("session shouldn't be null");
-        }
-        MediaNotification notification;
-        synchronized (mLock) {
-            if (mSessions.get(session.getId()) != session) {
-                // Session isn't added or removed already.
-                return;
-            }
-            mSessions.remove(session.getId());
-            notification = mNotifications.remove(session);
-        }
-        session.setForegroundServiceEventCallback(null);
-        if (notification != null) {
-            mNotificationManager.cancel(notification.getNotificationId());
-        }
-        if (getSessions().isEmpty()) {
-            stopForeground(false);
-        }
-    }
-
-    /**
-     * Gets the list of {@link MediaSession2}s that you've added to this service.
-     *
-     * @return sessions
-     */
-    public final @NonNull List<MediaSession2> getSessions() {
-        List<MediaSession2> list = new ArrayList<>();
-        synchronized (mLock) {
-            list.addAll(mSessions.values());
-        }
-        return list;
-    }
-
-    /**
-     * Returns the {@link MediaSessionManager}.
-     */
-    @NonNull
-    MediaSessionManager getMediaSessionManager() {
-        synchronized (mLock) {
-            return mMediaSessionManager;
-        }
-    }
-
-    /**
-     * Called by registered {@link MediaSession2.ForegroundServiceEventCallback}
-     *
-     * @param session session with change
-     * @param playbackActive {@code true} if playback is active.
-     */
-    void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {
-        MediaNotification mediaNotification = onUpdateNotification(session);
-        if (mediaNotification == null) {
-            // The service implementation doesn't want to use the automatic start/stopForeground
-            // feature.
-            return;
-        }
-        synchronized (mLock) {
-            mNotifications.put(session, mediaNotification);
-        }
-        int id = mediaNotification.getNotificationId();
-        Notification notification = mediaNotification.getNotification();
-        if (!playbackActive) {
-            mNotificationManager.notify(id, notification);
-            return;
-        }
-        // playbackActive == true
-        startForegroundService(mStartSelfIntent);
-        startForeground(id, notification);
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Returned by {@link #onUpdateNotification(MediaSession2)} for making session service
-     * foreground service to keep playback running in the background. It's highly recommended to
-     * show media style notification here.
-     */
-    public static class MediaNotification {
-        private final int mNotificationId;
-        private final Notification mNotification;
-
-        /**
-         * Default constructor
-         *
-         * @param notificationId notification id to be used for
-         *        {@link NotificationManager#notify(int, Notification)}.
-         * @param notification a notification to make session service run in the foreground. Media
-         *        style notification is recommended here.
-         */
-        public MediaNotification(int notificationId, @NonNull Notification notification) {
-            if (notification == null) {
-                throw new IllegalArgumentException("notification shouldn't be null");
-            }
-            mNotificationId = notificationId;
-            mNotification = notification;
-        }
-
-        /**
-         * Gets the id of the notification.
-         *
-         * @return the notification id
-         */
-        public int getNotificationId() {
-            return mNotificationId;
-        }
-
-        /**
-         * Gets the notification.
-         *
-         * @return the notification
-         */
-        @NonNull
-        public Notification getNotification() {
-            return mNotification;
-        }
-    }
-
-    private static final class MediaSession2ServiceStub extends IMediaSession2Service.Stub
-            implements AutoCloseable {
-        final WeakReference<MediaSession2Service> mService;
-        final Handler mHandler;
-
-        MediaSession2ServiceStub(MediaSession2Service service) {
-            mService = new WeakReference<>(service);
-            mHandler = new Handler(service.getMainLooper());
-        }
-
-        @Override
-        public void connect(Controller2Link caller, int seq, Bundle connectionRequest) {
-            if (mService.get() == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "Service is already destroyed");
-                }
-                return;
-            }
-            if (caller == null || connectionRequest == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "Ignoring calls with illegal arguments, caller=" + caller
-                            + ", connectionRequest=" + connectionRequest);
-                }
-                return;
-            }
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> {
-                    boolean shouldNotifyDisconnected = true;
-                    try {
-                        final MediaSession2Service service = mService.get();
-                        if (service == null) {
-                            if (DEBUG) {
-                                Log.d(TAG, "Service isn't available");
-                            }
-                            return;
-                        }
-
-                        String callingPkg = connectionRequest.getString(KEY_PACKAGE_NAME);
-                        // The Binder.getCallingPid() can be 0 for an oneway call from the
-                        // remote process. If it's the case, use PID from the connectionRequest.
-                        RemoteUserInfo remoteUserInfo = new RemoteUserInfo(
-                                callingPkg,
-                                pid == 0 ? connectionRequest.getInt(KEY_PID) : pid,
-                                uid);
-
-                        Bundle connectionHints = connectionRequest.getBundle(KEY_CONNECTION_HINTS);
-                        if (connectionHints == null) {
-                            Log.w(TAG, "connectionHints shouldn't be null.");
-                            connectionHints = Bundle.EMPTY;
-                        } else if (MediaSession2.hasCustomParcelable(connectionHints)) {
-                            Log.w(TAG, "connectionHints contain custom parcelable. Ignoring.");
-                            connectionHints = Bundle.EMPTY;
-                        }
-
-                        final ControllerInfo controllerInfo = new ControllerInfo(
-                                remoteUserInfo,
-                                service.getMediaSessionManager()
-                                        .isTrustedForMediaControl(remoteUserInfo),
-                                caller,
-                                connectionHints);
-
-                        if (DEBUG) {
-                            Log.d(TAG, "Handling incoming connection request from the"
-                                    + " controller=" + controllerInfo);
-                        }
-
-                        final MediaSession2 session;
-                        session = service.onGetSession(controllerInfo);
-
-                        if (session == null) {
-                            if (DEBUG) {
-                                Log.d(TAG, "Rejecting incoming connection request from the"
-                                        + " controller=" + controllerInfo);
-                            }
-                            // Note: Trusted controllers also can be rejected according to the
-                            // service implementation.
-                            return;
-                        }
-                        service.addSession(session);
-                        shouldNotifyDisconnected = false;
-                        session.onConnect(caller, pid, uid, seq, connectionRequest);
-                    } catch (Exception e) {
-                        // Don't propagate exception in service to the controller.
-                        Log.w(TAG, "Failed to add a session to session service", e);
-                    } finally {
-                        // Trick to call onDisconnected() in one place.
-                        if (shouldNotifyDisconnected) {
-                            if (DEBUG) {
-                                Log.d(TAG, "Notifying the controller of its disconnection");
-                            }
-                            try {
-                                caller.notifyDisconnected(0);
-                            } catch (RuntimeException e) {
-                                // Controller may be died prematurely.
-                                // Not an issue because we'll ignore it anyway.
-                            }
-                        }
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void close() {
-            mHandler.removeCallbacksAndMessages(null);
-            mService.clear();
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaTranscodingManager.java b/apex/media/framework/java/android/media/MediaTranscodingManager.java
deleted file mode 100644
index aff3204..0000000
--- a/apex/media/framework/java/android/media/MediaTranscodingManager.java
+++ /dev/null
@@ -1,1749 +0,0 @@
-/*
- * 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.media;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.net.Uri;
-import android.os.Build;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.system.Os;
-import android.util.Log;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.modules.annotation.MinSdk;
-import com.android.modules.utils.build.SdkLevel;
-
-import java.io.FileNotFoundException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- Android 12 introduces Compatible media transcoding feature.  See
- <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding">
- Compatible media transcoding</a>. MediaTranscodingManager provides an interface to the system's media
- transcoding service and can be used to transcode media files, e.g. transcoding a video from HEVC to
- AVC.
-
- <h3>Transcoding Types</h3>
- <h4>Video Transcoding</h4>
- When transcoding a video file, the video track will be transcoded based on the desired track format
- and the audio track will be pass through without any modification.
- <p class=note>
- Note that currently only support transcoding video file in mp4 format and with single video track.
-
- <h3>Transcoding Request</h3>
- <p>
- To transcode a media file, first create a {@link TranscodingRequest} through its builder class
- {@link VideoTranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through
- {@link MediaTranscodingManager#enqueueRequest(
-         TranscodingRequest, Executor, OnTranscodingFinishedListener)}
- TranscodeRequest are processed based on client process's priority and request priority. When a
- transcode operation is completed the caller is notified via its
- {@link OnTranscodingFinishedListener}.
- In the meantime the caller may use the returned TranscodingSession object to cancel or check the
- status of a specific transcode operation.
- <p>
- Here is an example where <code>Builder</code> is used to specify all parameters
-
- <pre class=prettyprint>
- VideoTranscodingRequest request =
-    new VideoTranscodingRequest.Builder(srcUri, dstUri, videoFormat).build();
- }</pre>
- @hide
- */
-@MinSdk(Build.VERSION_CODES.S)
-@RequiresApi(Build.VERSION_CODES.S)
-@SystemApi
-public final class MediaTranscodingManager {
-    private static final String TAG = "MediaTranscodingManager";
-
-    /** Maximum number of retry to connect to the service. */
-    private static final int CONNECT_SERVICE_RETRY_COUNT = 100;
-
-    /** Interval between trying to reconnect to the service. */
-    private static final int INTERVAL_CONNECT_SERVICE_RETRY_MS = 40;
-
-    /** Default bpp(bits-per-pixel) to use for calculating default bitrate. */
-    private static final float BPP = 0.25f;
-
-    /**
-     * Listener that gets notified when a transcoding operation has finished.
-     * This listener gets notified regardless of how the operation finished. It is up to the
-     * listener implementation to check the result and take appropriate action.
-     */
-    @FunctionalInterface
-    public interface OnTranscodingFinishedListener {
-        /**
-         * Called when the transcoding operation has finished. The receiver may use the
-         * TranscodingSession to check the result, i.e. whether the operation succeeded, was
-         * canceled or if an error occurred.
-         *
-         * @param session The TranscodingSession instance for the finished transcoding operation.
-         */
-        void onTranscodingFinished(@NonNull TranscodingSession session);
-    }
-
-    private final Context mContext;
-    private ContentResolver mContentResolver;
-    private final String mPackageName;
-    private final int mPid;
-    private final int mUid;
-    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
-    private final HashMap<Integer, TranscodingSession> mPendingTranscodingSessions = new HashMap();
-    private final Object mLock = new Object();
-    @GuardedBy("mLock")
-    @NonNull private ITranscodingClient mTranscodingClient = null;
-    private static MediaTranscodingManager sMediaTranscodingManager;
-
-    private void handleTranscodingFinished(int sessionId, TranscodingResultParcel result) {
-        synchronized (mPendingTranscodingSessions) {
-            // Gets the session associated with the sessionId and removes it from
-            // mPendingTranscodingSessions.
-            final TranscodingSession session = mPendingTranscodingSessions.remove(sessionId);
-
-            if (session == null) {
-                // This should not happen in reality.
-                Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
-                return;
-            }
-
-            // Updates the session status and result.
-            session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
-                    TranscodingSession.RESULT_SUCCESS,
-                    TranscodingSession.ERROR_NONE);
-
-            // Notifies client the session is done.
-            if (session.mListener != null && session.mListenerExecutor != null) {
-                session.mListenerExecutor.execute(
-                        () -> session.mListener.onTranscodingFinished(session));
-            }
-        }
-    }
-
-    private void handleTranscodingFailed(int sessionId, int errorCode) {
-        synchronized (mPendingTranscodingSessions) {
-            // Gets the session associated with the sessionId and removes it from
-            // mPendingTranscodingSessions.
-            final TranscodingSession session = mPendingTranscodingSessions.remove(sessionId);
-
-            if (session == null) {
-                // This should not happen in reality.
-                Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
-                return;
-            }
-
-            // Updates the session status and result.
-            session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
-                    TranscodingSession.RESULT_ERROR, errorCode);
-
-            // Notifies client the session failed.
-            if (session.mListener != null && session.mListenerExecutor != null) {
-                session.mListenerExecutor.execute(
-                        () -> session.mListener.onTranscodingFinished(session));
-            }
-        }
-    }
-
-    private void handleTranscodingProgressUpdate(int sessionId, int newProgress) {
-        synchronized (mPendingTranscodingSessions) {
-            // Gets the session associated with the sessionId.
-            final TranscodingSession session = mPendingTranscodingSessions.get(sessionId);
-
-            if (session == null) {
-                // This should not happen in reality.
-                Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
-                return;
-            }
-
-            // Updates the session progress.
-            session.updateProgress(newProgress);
-
-            // Notifies client the progress update.
-            if (session.mProgressUpdateExecutor != null
-                    && session.mProgressUpdateListener != null) {
-                session.mProgressUpdateExecutor.execute(
-                        () -> session.mProgressUpdateListener.onProgressUpdate(session,
-                                newProgress));
-            }
-        }
-    }
-
-    private IMediaTranscodingService getService(boolean retry) {
-        // Do not try to get the service on pre-S. The service is lazy-start and getting the
-        // service could block.
-        if (!SdkLevel.isAtLeastS()) {
-            return null;
-        }
-
-        int retryCount = !retry ? 1 :  CONNECT_SERVICE_RETRY_COUNT;
-        Log.i(TAG, "get service with retry " + retryCount);
-        for (int count = 1;  count <= retryCount; count++) {
-            Log.d(TAG, "Trying to connect to service. Try count: " + count);
-            IMediaTranscodingService service = IMediaTranscodingService.Stub.asInterface(
-                    MediaFrameworkInitializer
-                    .getMediaServiceManager()
-                    .getMediaTranscodingServiceRegisterer()
-                    .get());
-            if (service != null) {
-                return service;
-            }
-            try {
-                // Sleep a bit before retry.
-                Thread.sleep(INTERVAL_CONNECT_SERVICE_RETRY_MS);
-            } catch (InterruptedException ie) {
-                /* ignore */
-            }
-        }
-        Log.w(TAG, "Failed to get service");
-        return null;
-    }
-
-    /*
-     * Handle client binder died event.
-     * Upon receiving a binder died event of the client, we will do the following:
-     * 1) For the session that is running, notify the client that the session is failed with
-     *    error code,  so client could choose to retry the session or not.
-     *    TODO(hkuang): Add a new error code to signal service died error.
-     * 2) For the sessions that is still pending or paused, we will resubmit the session
-     *    once we successfully reconnect to the service and register a new client.
-     * 3) When trying to connect to the service and register a new client. The service may need time
-     *    to reboot or never boot up again. So we will retry for a number of times. If we still
-     *    could not connect, we will notify client session failure for the pending and paused
-     *    sessions.
-     */
-    private void onClientDied() {
-        synchronized (mLock) {
-            mTranscodingClient = null;
-        }
-
-        // Delegates the session notification and retry to the executor as it may take some time.
-        mExecutor.execute(() -> {
-            // List to track the sessions that we want to retry.
-            List<TranscodingSession> retrySessions = new ArrayList<TranscodingSession>();
-
-            // First notify the client of session failure for all the running sessions.
-            synchronized (mPendingTranscodingSessions) {
-                for (Map.Entry<Integer, TranscodingSession> entry :
-                        mPendingTranscodingSessions.entrySet()) {
-                    TranscodingSession session = entry.getValue();
-
-                    if (session.getStatus() == TranscodingSession.STATUS_RUNNING) {
-                        session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
-                                TranscodingSession.RESULT_ERROR,
-                                TranscodingSession.ERROR_SERVICE_DIED);
-
-                        // Remove the session from pending sessions.
-                        mPendingTranscodingSessions.remove(entry.getKey());
-
-                        if (session.mListener != null && session.mListenerExecutor != null) {
-                            Log.i(TAG, "Notify client session failed");
-                            session.mListenerExecutor.execute(
-                                    () -> session.mListener.onTranscodingFinished(session));
-                        }
-                    } else if (session.getStatus() == TranscodingSession.STATUS_PENDING
-                            || session.getStatus() == TranscodingSession.STATUS_PAUSED) {
-                        // Add the session to retrySessions to handle them later.
-                        retrySessions.add(session);
-                    }
-                }
-            }
-
-            // Try to register with the service once it boots up.
-            IMediaTranscodingService service = getService(true /*retry*/);
-            boolean haveTranscodingClient = false;
-            if (service != null) {
-                synchronized (mLock) {
-                    mTranscodingClient = registerClient(service);
-                    if (mTranscodingClient != null) {
-                        haveTranscodingClient = true;
-                    }
-                }
-            }
-
-            for (TranscodingSession session : retrySessions) {
-                // Notify the session failure if we fails to connect to the service or fail
-                // to retry the session.
-                if (!haveTranscodingClient) {
-                    // TODO(hkuang): Return correct error code to the client.
-                    handleTranscodingFailed(session.getSessionId(), 0 /*unused */);
-                }
-
-                try {
-                    // Do not set hasRetried for retry initiated by MediaTranscodingManager.
-                    session.retryInternal(false /*setHasRetried*/);
-                } catch (Exception re) {
-                    // TODO(hkuang): Return correct error code to the client.
-                    handleTranscodingFailed(session.getSessionId(), 0 /*unused */);
-                }
-            }
-        });
-    }
-
-    private void updateStatus(int sessionId, int status) {
-        synchronized (mPendingTranscodingSessions) {
-            final TranscodingSession session = mPendingTranscodingSessions.get(sessionId);
-
-            if (session == null) {
-                // This should not happen in reality.
-                Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
-                return;
-            }
-
-            // Updates the session status.
-            session.updateStatus(status);
-        }
-    }
-
-    // Just forwards all the events to the event handler.
-    private ITranscodingClientCallback mTranscodingClientCallback =
-            new ITranscodingClientCallback.Stub() {
-                // TODO(hkuang): Add more unit test to test difference file open mode.
-                @Override
-                public ParcelFileDescriptor openFileDescriptor(String fileUri, String mode)
-                        throws RemoteException {
-                    if (!mode.equals("r") && !mode.equals("w") && !mode.equals("rw")) {
-                        Log.e(TAG, "Unsupport mode: " + mode);
-                        return null;
-                    }
-
-                    Uri uri = Uri.parse(fileUri);
-                    try {
-                        AssetFileDescriptor afd = mContentResolver.openAssetFileDescriptor(uri,
-                                mode);
-                        if (afd != null) {
-                            return afd.getParcelFileDescriptor();
-                        }
-                    } catch (FileNotFoundException e) {
-                        Log.w(TAG, "Cannot find content uri: " + uri, e);
-                    } catch (SecurityException e) {
-                        Log.w(TAG, "Cannot open content uri: " + uri, e);
-                    } catch (Exception e) {
-                        Log.w(TAG, "Unknown content uri: " + uri, e);
-                    }
-                    return null;
-                }
-
-                @Override
-                public void onTranscodingStarted(int sessionId) throws RemoteException {
-                    updateStatus(sessionId, TranscodingSession.STATUS_RUNNING);
-                }
-
-                @Override
-                public void onTranscodingPaused(int sessionId) throws RemoteException {
-                    updateStatus(sessionId, TranscodingSession.STATUS_PAUSED);
-                }
-
-                @Override
-                public void onTranscodingResumed(int sessionId) throws RemoteException {
-                    updateStatus(sessionId, TranscodingSession.STATUS_RUNNING);
-                }
-
-                @Override
-                public void onTranscodingFinished(int sessionId, TranscodingResultParcel result)
-                        throws RemoteException {
-                    handleTranscodingFinished(sessionId, result);
-                }
-
-                @Override
-                public void onTranscodingFailed(int sessionId, int errorCode)
-                        throws RemoteException {
-                    handleTranscodingFailed(sessionId, errorCode);
-                }
-
-                @Override
-                public void onAwaitNumberOfSessionsChanged(int sessionId, int oldAwaitNumber,
-                        int newAwaitNumber) throws RemoteException {
-                    //TODO(hkuang): Implement this.
-                }
-
-                @Override
-                public void onProgressUpdate(int sessionId, int newProgress)
-                        throws RemoteException {
-                    handleTranscodingProgressUpdate(sessionId, newProgress);
-                }
-            };
-
-    private ITranscodingClient registerClient(IMediaTranscodingService service) {
-        synchronized (mLock) {
-            try {
-                // Registers the client with MediaTranscoding service.
-                mTranscodingClient = service.registerClient(
-                        mTranscodingClientCallback,
-                        mPackageName,
-                        mPackageName);
-
-                if (mTranscodingClient != null) {
-                    mTranscodingClient.asBinder().linkToDeath(() -> onClientDied(), /* flags */ 0);
-                }
-            } catch (Exception ex) {
-                Log.e(TAG, "Failed to register new client due to exception " + ex);
-                mTranscodingClient = null;
-            }
-        }
-        return mTranscodingClient;
-    }
-
-    /**
-     * @hide
-     */
-    public MediaTranscodingManager(@NonNull Context context) {
-        mContext = context;
-        mContentResolver = mContext.getContentResolver();
-        mPackageName = mContext.getPackageName();
-        mUid = Os.getuid();
-        mPid = Os.getpid();
-    }
-
-    /**
-     * Abstract base class for all the TranscodingRequest.
-     * <p> TranscodingRequest encapsulates the desired configuration for the transcoding.
-     */
-    public abstract static class TranscodingRequest {
-        /**
-         *
-         * Default transcoding type.
-         * @hide
-         */
-        public static final int TRANSCODING_TYPE_UNKNOWN = 0;
-
-        /**
-         * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video.
-         * <p>Note that currently only support transcoding video file in mp4 format.
-         * @hide
-         */
-        public static final int TRANSCODING_TYPE_VIDEO = 1;
-
-        /**
-         * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image.
-         * @hide
-         */
-        public static final int TRANSCODING_TYPE_IMAGE = 2;
-
-        /** @hide */
-        @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = {
-                TRANSCODING_TYPE_UNKNOWN,
-                TRANSCODING_TYPE_VIDEO,
-                TRANSCODING_TYPE_IMAGE,
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface TranscodingType {}
-
-        /**
-         * Default value.
-         *
-         * @hide
-         */
-        public static final int PRIORITY_UNKNOWN = 0;
-        /**
-         * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the
-         * client wants the transcoding result as soon as possible.
-         * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve
-         * performance penalty due to resource reallocation to prioritize the sessions with higher
-         * priority.
-         *
-         * @hide
-         */
-        public static final int PRIORITY_REALTIME = 1;
-
-        /**
-         * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not
-         * need the transcoding result as soon as possible.
-         * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set
-         * to
-         * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept
-         * delay of the transcoding result.
-         *
-         * @hide
-         *
-         */
-        public static final int PRIORITY_OFFLINE = 2;
-
-        /** @hide */
-        @IntDef(prefix = {"PRIORITY_"}, value = {
-                PRIORITY_UNKNOWN,
-                PRIORITY_REALTIME,
-                PRIORITY_OFFLINE,
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface TranscodingPriority {}
-
-        /** Uri of the source media file. */
-        private @NonNull Uri mSourceUri;
-
-        /** Uri of the destination media file. */
-        private @NonNull Uri mDestinationUri;
-
-        /** FileDescriptor of the source media file. */
-        private @Nullable ParcelFileDescriptor mSourceFileDescriptor;
-
-        /** FileDescriptor of the destination media file. */
-        private @Nullable ParcelFileDescriptor mDestinationFileDescriptor;
-
-        /**
-         *  The UID of the client that the TranscodingRequest is for. Only privileged caller could
-         *  set this Uid as only they could do the transcoding on behalf of the client.
-         *  -1 means not available.
-         */
-        private int mClientUid = -1;
-
-        /**
-         *  The Pid of the client that the TranscodingRequest is for. Only privileged caller could
-         *  set this Uid as only they could do the transcoding on behalf of the client.
-         *  -1 means not available.
-         */
-        private int mClientPid = -1;
-
-        /** Type of the transcoding. */
-        private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
-
-        /** Priority of the transcoding. */
-        private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN;
-
-        /**
-         * Desired image format for the destination file.
-         * <p> If this is null, source file's image track will be passed through and copied to the
-         * destination file.
-         * @hide
-         */
-        private @Nullable MediaFormat mImageFormat = null;
-
-        @VisibleForTesting
-        private TranscodingTestConfig mTestConfig = null;
-
-        /**
-         * Prevent public constructor access.
-         */
-        /* package private */ TranscodingRequest() {
-        }
-
-        private TranscodingRequest(Builder b) {
-            mSourceUri = b.mSourceUri;
-            mSourceFileDescriptor = b.mSourceFileDescriptor;
-            mDestinationUri = b.mDestinationUri;
-            mDestinationFileDescriptor = b.mDestinationFileDescriptor;
-            mClientUid = b.mClientUid;
-            mClientPid = b.mClientPid;
-            mPriority = b.mPriority;
-            mType = b.mType;
-            mTestConfig = b.mTestConfig;
-        }
-
-        /**
-         * Return the type of the transcoding.
-         * @hide
-         */
-        @TranscodingType
-        public int getType() {
-            return mType;
-        }
-
-        /** Return source uri of the transcoding. */
-        @NonNull
-        public Uri getSourceUri() {
-            return mSourceUri;
-        }
-
-        /**
-         * Return source file descriptor of the transcoding.
-         * This will be null if client has not provided it.
-         */
-        @Nullable
-        public ParcelFileDescriptor getSourceFileDescriptor() {
-            return mSourceFileDescriptor;
-        }
-
-        /** Return the UID of the client that this request is for. -1 means not available. */
-        public int getClientUid() {
-            return mClientUid;
-        }
-
-        /** Return the PID of the client that this request is for. -1 means not available. */
-        public int getClientPid() {
-            return mClientPid;
-        }
-
-        /** Return destination uri of the transcoding. */
-        @NonNull
-        public Uri getDestinationUri() {
-            return mDestinationUri;
-        }
-
-        /**
-         * Return destination file descriptor of the transcoding.
-         * This will be null if client has not provided it.
-         */
-        @Nullable
-        public ParcelFileDescriptor getDestinationFileDescriptor() {
-            return mDestinationFileDescriptor;
-        }
-
-        /**
-         * Return priority of the transcoding.
-         * @hide
-         */
-        @TranscodingPriority
-        public int getPriority() {
-            return mPriority;
-        }
-
-        /**
-         * Return TestConfig of the transcoding.
-         * @hide
-         */
-        @Nullable
-        public TranscodingTestConfig getTestConfig() {
-            return mTestConfig;
-        }
-
-        abstract void writeFormatToParcel(TranscodingRequestParcel parcel);
-
-        /* Writes the TranscodingRequest to a parcel. */
-        private TranscodingRequestParcel writeToParcel(@NonNull Context context) {
-            TranscodingRequestParcel parcel = new TranscodingRequestParcel();
-            switch (mPriority) {
-            case PRIORITY_OFFLINE:
-                parcel.priority = TranscodingSessionPriority.kUnspecified;
-                break;
-            case PRIORITY_REALTIME:
-            case PRIORITY_UNKNOWN:
-            default:
-                parcel.priority = TranscodingSessionPriority.kNormal;
-                break;
-            }
-            parcel.transcodingType = mType;
-            parcel.sourceFilePath = mSourceUri.toString();
-            parcel.sourceFd = mSourceFileDescriptor;
-            parcel.destinationFilePath = mDestinationUri.toString();
-            parcel.destinationFd = mDestinationFileDescriptor;
-            parcel.clientUid = mClientUid;
-            parcel.clientPid = mClientPid;
-            if (mClientUid < 0) {
-                parcel.clientPackageName = context.getPackageName();
-            } else {
-                String packageName = context.getPackageManager().getNameForUid(mClientUid);
-                // PackageName is optional as some uid does not have package name. Set to
-                // "Unavailable" string in this case.
-                if (packageName == null) {
-                    Log.w(TAG, "Failed to find package for uid: " + mClientUid);
-                    packageName = "Unavailable";
-                }
-                parcel.clientPackageName = packageName;
-            }
-            writeFormatToParcel(parcel);
-            if (mTestConfig != null) {
-                parcel.isForTesting = true;
-                parcel.testConfig = mTestConfig;
-            }
-            return parcel;
-        }
-
-        /**
-         * Builder to build a {@link TranscodingRequest} object.
-         *
-         * @param <T> The subclass to be built.
-         */
-        abstract static class Builder<T extends Builder<T>> {
-            private @NonNull Uri mSourceUri;
-            private @NonNull Uri mDestinationUri;
-            private @Nullable ParcelFileDescriptor mSourceFileDescriptor = null;
-            private @Nullable ParcelFileDescriptor mDestinationFileDescriptor = null;
-            private int mClientUid = -1;
-            private int mClientPid = -1;
-            private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
-            private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN;
-            private TranscodingTestConfig mTestConfig;
-
-            abstract T self();
-
-            /**
-             * Creates a builder for building {@link TranscodingRequest}s.
-             *
-             * Client must set the source Uri. If client also provides the source fileDescriptor
-             * through is provided by {@link #setSourceFileDescriptor(ParcelFileDescriptor)},
-             * TranscodingSession will use the fd instead of calling back to the client to open the
-             * sourceUri.
-             *
-             *
-             * @param type The transcoding type.
-             * @param sourceUri Content uri for the source media file.
-             * @param destinationUri Content uri for the destination media file.
-             *
-             */
-            private Builder(@TranscodingType int type, @NonNull Uri sourceUri,
-                    @NonNull Uri destinationUri) {
-                mType = type;
-
-                if (sourceUri == null || Uri.EMPTY.equals(sourceUri)) {
-                    throw new IllegalArgumentException(
-                            "You must specify a non-empty source Uri.");
-                }
-                mSourceUri = sourceUri;
-
-                if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) {
-                    throw new IllegalArgumentException(
-                            "You must specify a non-empty destination Uri.");
-                }
-                mDestinationUri = destinationUri;
-            }
-
-            /**
-             * Specifies the fileDescriptor opened from the source media file.
-             *
-             * This call is optional. If the source fileDescriptor is provided, TranscodingSession
-             * will use it directly instead of opening the uri from {@link #Builder(int, Uri, Uri)}.
-             * It is client's responsibility to make sure the fileDescriptor is opened from the
-             * source uri.
-             * @param fileDescriptor a {@link ParcelFileDescriptor} opened from source media file.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if fileDescriptor is invalid.
-             */
-            @NonNull
-            public T setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) {
-                if (fileDescriptor == null || fileDescriptor.getFd() < 0) {
-                    throw new IllegalArgumentException(
-                            "Invalid source descriptor.");
-                }
-                mSourceFileDescriptor = fileDescriptor;
-                return self();
-            }
-
-            /**
-             * Specifies the fileDescriptor opened from the destination media file.
-             *
-             * This call is optional. If the destination fileDescriptor is provided,
-             * TranscodingSession will use it directly instead of opening the source uri from
-             * {@link #Builder(int, Uri, Uri)} upon transcoding starts. It is client's
-             * responsibility to make sure the fileDescriptor is opened from the destination uri.
-             * @param fileDescriptor a {@link ParcelFileDescriptor} opened from destination media
-             *                       file.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if fileDescriptor is invalid.
-             */
-            @NonNull
-            public T setDestinationFileDescriptor(
-                    @NonNull ParcelFileDescriptor fileDescriptor) {
-                if (fileDescriptor == null || fileDescriptor.getFd() < 0) {
-                    throw new IllegalArgumentException(
-                            "Invalid destination descriptor.");
-                }
-                mDestinationFileDescriptor = fileDescriptor;
-                return self();
-            }
-
-            /**
-             * Specify the UID of the client that this request is for.
-             * <p>
-             * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the
-             * pid. Note that the permission check happens on the service side upon starting the
-             * transcoding. If the client does not have the permission, the transcoding will fail.
-             *
-             * @param uid client Uid.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if uid is invalid.
-             */
-            @NonNull
-            public T setClientUid(int uid) {
-                if (uid < 0) {
-                    throw new IllegalArgumentException("Invalid Uid");
-                }
-                mClientUid = uid;
-                return self();
-            }
-
-            /**
-             * Specify the pid of the client that this request is for.
-             * <p>
-             * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the
-             * pid. Note that the permission check happens on the service side upon starting the
-             * transcoding. If the client does not have the permission, the transcoding will fail.
-             *
-             * @param pid client Pid.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if pid is invalid.
-             */
-            @NonNull
-            public T setClientPid(int pid) {
-                if (pid < 0) {
-                    throw new IllegalArgumentException("Invalid pid");
-                }
-                mClientPid = pid;
-                return self();
-            }
-
-            /**
-             * Specifies the priority of the transcoding.
-             *
-             * @param priority Must be one of the {@code PRIORITY_*}
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if flags is invalid.
-             * @hide
-             */
-            @NonNull
-            public T setPriority(@TranscodingPriority int priority) {
-                if (priority != PRIORITY_OFFLINE && priority != PRIORITY_REALTIME) {
-                    throw new IllegalArgumentException("Invalid priority: " + priority);
-                }
-                mPriority = priority;
-                return self();
-            }
-
-            /**
-             * Sets the delay in processing this request.
-             * @param config test config.
-             * @return The same builder instance.
-             * @hide
-             */
-            @VisibleForTesting
-            @NonNull
-            public T setTestConfig(@NonNull TranscodingTestConfig config) {
-                mTestConfig = config;
-                return self();
-            }
-        }
-
-        /**
-         * Abstract base class for all the format resolvers.
-         */
-        abstract static class MediaFormatResolver {
-            private @NonNull ApplicationMediaCapabilities mClientCaps;
-
-            /**
-             * Prevents public constructor access.
-             */
-            /* package private */ MediaFormatResolver() {
-            }
-
-            /**
-             * Constructs MediaFormatResolver object.
-             *
-             * @param clientCaps An ApplicationMediaCapabilities object containing the client's
-             *                   capabilities.
-             */
-            MediaFormatResolver(@NonNull ApplicationMediaCapabilities clientCaps) {
-                if (clientCaps == null) {
-                    throw new IllegalArgumentException("Client capabilities must not be null");
-                }
-                mClientCaps = clientCaps;
-            }
-
-            /**
-             * Returns the client capabilities.
-             */
-            @NonNull
-            /* package */ ApplicationMediaCapabilities getClientCapabilities() {
-                return mClientCaps;
-            }
-
-            abstract boolean shouldTranscode();
-        }
-
-        /**
-         * VideoFormatResolver for deciding if video transcoding is needed, and if so, the track
-         * formats to use.
-         */
-        public static class VideoFormatResolver extends MediaFormatResolver {
-            private static final int BIT_RATE = 20000000;            // 20Mbps
-
-            private MediaFormat mSrcVideoFormatHint;
-            private MediaFormat mSrcAudioFormatHint;
-
-            /**
-             * Constructs a new VideoFormatResolver object.
-             *
-             * @param clientCaps An ApplicationMediaCapabilities object containing the client's
-             *                   capabilities.
-             * @param srcVideoFormatHint A MediaFormat object containing information about the
-             *                           source's video track format that could affect the
-             *                           transcoding decision. Such information could include video
-             *                           codec types, color spaces, whether special format info (eg.
-             *                           slow-motion markers) are present, etc.. If a particular
-             *                           information is not present, it will not be used to make the
-             *                           decision.
-             */
-            public VideoFormatResolver(@NonNull ApplicationMediaCapabilities clientCaps,
-                    @NonNull MediaFormat srcVideoFormatHint) {
-                super(clientCaps);
-                mSrcVideoFormatHint = srcVideoFormatHint;
-            }
-
-            /**
-             * Constructs a new VideoFormatResolver object.
-             *
-             * @param clientCaps An ApplicationMediaCapabilities object containing the client's
-             *                   capabilities.
-             * @param srcVideoFormatHint A MediaFormat object containing information about the
-             *                           source's video track format that could affect the
-             *                           transcoding decision. Such information could include video
-             *                           codec types, color spaces, whether special format info (eg.
-             *                           slow-motion markers) are present, etc.. If a particular
-             *                           information is not present, it will not be used to make the
-             *                           decision.
-             * @param srcAudioFormatHint A MediaFormat object containing information about the
-             *                           source's audio track format that could affect the
-             *                           transcoding decision.
-             * @hide
-             */
-            VideoFormatResolver(@NonNull ApplicationMediaCapabilities clientCaps,
-                    @NonNull MediaFormat srcVideoFormatHint,
-                    @NonNull MediaFormat srcAudioFormatHint) {
-                super(clientCaps);
-                mSrcVideoFormatHint = srcVideoFormatHint;
-                mSrcAudioFormatHint = srcAudioFormatHint;
-            }
-
-            /**
-             * Returns whether the source content should be transcoded.
-             *
-             * @return true if the source should be transcoded.
-             */
-            public boolean shouldTranscode() {
-                boolean supportHevc = getClientCapabilities().isVideoMimeTypeSupported(
-                        MediaFormat.MIMETYPE_VIDEO_HEVC);
-                if (!supportHevc && MediaFormat.MIMETYPE_VIDEO_HEVC.equals(
-                        mSrcVideoFormatHint.getString(MediaFormat.KEY_MIME))) {
-                    return true;
-                }
-                // TODO: add more checks as needed below.
-                return false;
-            }
-
-            /**
-             * Retrieves the video track format to be used on
-             * {@link VideoTranscodingRequest.Builder#setVideoTrackFormat(MediaFormat)} for this
-             * configuration.
-             *
-             * @return the video track format to be used if transcoding should be performed,
-             *         and null otherwise.
-             * @throws IllegalArgumentException if the hinted source video format contains invalid
-             *         parameters.
-             */
-            @Nullable
-            public MediaFormat resolveVideoFormat() {
-                if (!shouldTranscode()) {
-                    return null;
-                }
-
-                MediaFormat videoTrackFormat = new MediaFormat(mSrcVideoFormatHint);
-                videoTrackFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);
-
-                int width = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_WIDTH, -1);
-                int height = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_HEIGHT, -1);
-                if (width <= 0 || height <= 0) {
-                    throw new IllegalArgumentException(
-                            "Source Width and height must be larger than 0");
-                }
-
-                float frameRate =
-                        mSrcVideoFormatHint.getNumber(MediaFormat.KEY_FRAME_RATE, 30.0)
-                        .floatValue();
-                if (frameRate <= 0) {
-                    throw new IllegalArgumentException(
-                            "frameRate must be larger than 0");
-                }
-
-                int bitrate = getAVCBitrate(width, height, frameRate);
-                videoTrackFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
-                return videoTrackFormat;
-            }
-
-            /**
-             * Generate a default bitrate with the fixed bpp(bits-per-pixel) 0.25.
-             * This maps to:
-             * 1080P@30fps -> 16Mbps
-             * 1080P@60fps-> 32Mbps
-             * 4K@30fps -> 62Mbps
-             */
-            private static int getDefaultBitrate(int width, int height, float frameRate) {
-                return (int) (width * height * frameRate * BPP);
-            }
-
-            /**
-             * Query the bitrate from CamcorderProfile. If there are two profiles that match the
-             * width/height/framerate, we will use the higher one to get better quality.
-             * Return default bitrate if could not find any match profile.
-             */
-            private static int getAVCBitrate(int width, int height, float frameRate) {
-                int bitrate = -1;
-                int[] cameraIds = {0, 1};
-
-                // Profiles ordered in decreasing order of preference.
-                int[] preferQualities = {
-                        CamcorderProfile.QUALITY_2160P,
-                        CamcorderProfile.QUALITY_1080P,
-                        CamcorderProfile.QUALITY_720P,
-                        CamcorderProfile.QUALITY_480P,
-                        CamcorderProfile.QUALITY_LOW,
-                };
-
-                for (int cameraId : cameraIds) {
-                    for (int quality : preferQualities) {
-                        // Check if camera id has profile for the quality level.
-                        if (!CamcorderProfile.hasProfile(cameraId, quality)) {
-                            continue;
-                        }
-                        CamcorderProfile profile = CamcorderProfile.get(cameraId, quality);
-                        // Check the width/height/framerate/codec, also consider portrait case.
-                        if (((width == profile.videoFrameWidth
-                                && height == profile.videoFrameHeight)
-                                || (height == profile.videoFrameWidth
-                                && width == profile.videoFrameHeight))
-                                && (int) frameRate == profile.videoFrameRate
-                                && profile.videoCodec == MediaRecorder.VideoEncoder.H264) {
-                            if (bitrate < profile.videoBitRate) {
-                                bitrate = profile.videoBitRate;
-                            }
-                            break;
-                        }
-                    }
-                }
-
-                if (bitrate == -1) {
-                    Log.w(TAG, "Failed to find CamcorderProfile for w: " + width + "h: " + height
-                            + " fps: "
-                            + frameRate);
-                    bitrate = getDefaultBitrate(width, height, frameRate);
-                }
-                Log.d(TAG, "Using bitrate " + bitrate + " for " + width + " " + height + " "
-                        + frameRate);
-                return bitrate;
-            }
-
-            /**
-             * Retrieves the audio track format to be used for transcoding.
-             *
-             * @return the audio track format to be used if transcoding should be performed, and
-             *         null otherwise.
-             * @hide
-             */
-            @Nullable
-            public MediaFormat resolveAudioFormat() {
-                if (!shouldTranscode()) {
-                    return null;
-                }
-                // Audio transcoding is not supported yet, always return null.
-                return null;
-            }
-        }
-    }
-
-    /**
-     * VideoTranscodingRequest encapsulates the configuration for transcoding a video.
-     */
-    public static final class VideoTranscodingRequest extends TranscodingRequest {
-        /**
-         * Desired output video format of the destination file.
-         * <p> If this is null, source file's video track will be passed through and copied to the
-         * destination file.
-         */
-        private @Nullable MediaFormat mVideoTrackFormat = null;
-
-        /**
-         * Desired output audio format of the destination file.
-         * <p> If this is null, source file's audio track will be passed through and copied to the
-         * destination file.
-         */
-        private @Nullable MediaFormat mAudioTrackFormat = null;
-
-        private VideoTranscodingRequest(VideoTranscodingRequest.Builder builder) {
-            super(builder);
-            mVideoTrackFormat = builder.mVideoTrackFormat;
-            mAudioTrackFormat = builder.mAudioTrackFormat;
-        }
-
-        /**
-         * Return the video track format of the transcoding.
-         * This will be null if client has not specified the video track format.
-         */
-        @NonNull
-        public MediaFormat getVideoTrackFormat() {
-            return mVideoTrackFormat;
-        }
-
-        @Override
-        void writeFormatToParcel(TranscodingRequestParcel parcel) {
-            parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat);
-        }
-
-        /* Converts the MediaFormat to TranscodingVideoTrackFormat. */
-        private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) {
-            if (format == null) {
-                throw new IllegalArgumentException("Invalid MediaFormat");
-            }
-
-            TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat();
-
-            if (format.containsKey(MediaFormat.KEY_MIME)) {
-                String mime = format.getString(MediaFormat.KEY_MIME);
-                if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) {
-                    trackFormat.codecType = TranscodingVideoCodecType.kAvc;
-                } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) {
-                    trackFormat.codecType = TranscodingVideoCodecType.kHevc;
-                } else {
-                    throw new UnsupportedOperationException("Only support transcode to avc/hevc");
-                }
-            }
-
-            if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
-                int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE);
-                if (bitrateBps <= 0) {
-                    throw new IllegalArgumentException("Bitrate must be larger than 0");
-                }
-                trackFormat.bitrateBps = bitrateBps;
-            }
-
-            if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey(
-                    MediaFormat.KEY_HEIGHT)) {
-                int width = format.getInteger(MediaFormat.KEY_WIDTH);
-                int height = format.getInteger(MediaFormat.KEY_HEIGHT);
-                if (width <= 0 || height <= 0) {
-                    throw new IllegalArgumentException("Width and height must be larger than 0");
-                }
-                // TODO: Validate the aspect ratio after adding scaling.
-                trackFormat.width = width;
-                trackFormat.height = height;
-            }
-
-            if (format.containsKey(MediaFormat.KEY_PROFILE)) {
-                int profile = format.getInteger(MediaFormat.KEY_PROFILE);
-                if (profile <= 0) {
-                    throw new IllegalArgumentException("Invalid codec profile");
-                }
-                // TODO: Validate the profile according to codec type.
-                trackFormat.profile = profile;
-            }
-
-            if (format.containsKey(MediaFormat.KEY_LEVEL)) {
-                int level = format.getInteger(MediaFormat.KEY_LEVEL);
-                if (level <= 0) {
-                    throw new IllegalArgumentException("Invalid codec level");
-                }
-                // TODO: Validate the level according to codec type.
-                trackFormat.level = level;
-            }
-
-            return trackFormat;
-        }
-
-        /**
-         * Builder class for {@link VideoTranscodingRequest}.
-         */
-        public static final class Builder extends
-                TranscodingRequest.Builder<VideoTranscodingRequest.Builder> {
-            /**
-             * Desired output video format of the destination file.
-             * <p> If this is null, source file's video track will be passed through and
-             * copied to the destination file.
-             */
-            private @Nullable MediaFormat mVideoTrackFormat = null;
-
-            /**
-             * Desired output audio format of the destination file.
-             * <p> If this is null, source file's audio track will be passed through and copied
-             * to the destination file.
-             */
-            private @Nullable MediaFormat mAudioTrackFormat = null;
-
-            /**
-             * Creates a builder for building {@link VideoTranscodingRequest}s.
-             *
-             * <p> Client could only specify the settings that matters to them, e.g. codec format or
-             * bitrate. And by default, transcoding will preserve the original video's settings
-             * (bitrate, framerate, resolution) if not provided.
-             * <p>Note that some settings may silently fail to apply if the device does not support
-             * them.
-             * @param sourceUri Content uri for the source media file.
-             * @param destinationUri Content uri for the destination media file.
-             * @param videoFormat MediaFormat containing the settings that client wants override in
-             *                    the original video's video track.
-             * @throws IllegalArgumentException if videoFormat is invalid.
-             */
-            public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri,
-                    @NonNull MediaFormat videoFormat) {
-                super(TRANSCODING_TYPE_VIDEO, sourceUri, destinationUri);
-                setVideoTrackFormat(videoFormat);
-            }
-
-            @Override
-            @NonNull
-            public Builder setClientUid(int uid) {
-                super.setClientUid(uid);
-                return self();
-            }
-
-            @Override
-            @NonNull
-            public Builder setClientPid(int pid) {
-                super.setClientPid(pid);
-                return self();
-            }
-
-            @Override
-            @NonNull
-            public Builder setSourceFileDescriptor(@NonNull ParcelFileDescriptor fd) {
-                super.setSourceFileDescriptor(fd);
-                return self();
-            }
-
-            @Override
-            @NonNull
-            public Builder setDestinationFileDescriptor(@NonNull ParcelFileDescriptor fd) {
-                super.setDestinationFileDescriptor(fd);
-                return self();
-            }
-
-            private void setVideoTrackFormat(@NonNull MediaFormat videoFormat) {
-                if (videoFormat == null) {
-                    throw new IllegalArgumentException("videoFormat must not be null");
-                }
-
-                // Check if the MediaFormat is for video by looking at the MIME type.
-                String mime = videoFormat.containsKey(MediaFormat.KEY_MIME)
-                        ? videoFormat.getString(MediaFormat.KEY_MIME) : null;
-                if (mime == null || !mime.startsWith("video/")) {
-                    throw new IllegalArgumentException("Invalid video format: wrong mime type");
-                }
-
-                mVideoTrackFormat = videoFormat;
-            }
-
-            /**
-             * @return a new {@link TranscodingRequest} instance successfully initialized
-             * with all the parameters set on this <code>Builder</code>.
-             * @throws UnsupportedOperationException if the parameters set on the
-             *                                       <code>Builder</code> were incompatible, or
-             *                                       if they are not supported by the
-             *                                       device.
-             */
-            @NonNull
-            public VideoTranscodingRequest build() {
-                return new VideoTranscodingRequest(this);
-            }
-
-            @Override
-            VideoTranscodingRequest.Builder self() {
-                return this;
-            }
-        }
-    }
-
-    /**
-     * Handle to an enqueued transcoding operation. An instance of this class represents a single
-     * enqueued transcoding operation. The caller can use that instance to query the status or
-     * progress, and to get the result once the operation has completed.
-     */
-    public static final class TranscodingSession {
-        /** The session is enqueued but not yet running. */
-        public static final int STATUS_PENDING = 1;
-        /** The session is currently running. */
-        public static final int STATUS_RUNNING = 2;
-        /** The session is finished. */
-        public static final int STATUS_FINISHED = 3;
-        /** The session is paused. */
-        public static final int STATUS_PAUSED = 4;
-
-        /** @hide */
-        @IntDef(prefix = { "STATUS_" }, value = {
-                STATUS_PENDING,
-                STATUS_RUNNING,
-                STATUS_FINISHED,
-                STATUS_PAUSED,
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface Status {}
-
-        /** The session does not have a result yet. */
-        public static final int RESULT_NONE = 1;
-        /** The session completed successfully. */
-        public static final int RESULT_SUCCESS = 2;
-        /** The session encountered an error while running. */
-        public static final int RESULT_ERROR = 3;
-        /** The session was canceled by the caller. */
-        public static final int RESULT_CANCELED = 4;
-
-        /** @hide */
-        @IntDef(prefix = { "RESULT_" }, value = {
-                RESULT_NONE,
-                RESULT_SUCCESS,
-                RESULT_ERROR,
-                RESULT_CANCELED,
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface Result {}
-
-
-        // The error code exposed here should be in sync with:
-        // frameworks/av/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
-        /** @hide */
-        @IntDef(prefix = { "TRANSCODING_SESSION_ERROR_" }, value = {
-                ERROR_NONE,
-                ERROR_DROPPED_BY_SERVICE,
-                ERROR_SERVICE_DIED})
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface TranscodingSessionErrorCode{}
-        /**
-         * Constant indicating that no error occurred.
-         */
-        public static final int ERROR_NONE = 0;
-
-        /**
-         * Constant indicating that the session is dropped by Transcoding service due to hitting
-         * the limit, e.g. too many back to back transcoding happen in a short time frame.
-         */
-        public static final int ERROR_DROPPED_BY_SERVICE = 1;
-
-        /**
-         * Constant indicating the backing transcoding service is died. Client should enqueue the
-         * the request again.
-         */
-        public static final int ERROR_SERVICE_DIED = 2;
-
-        /** Listener that gets notified when the progress changes. */
-        @FunctionalInterface
-        public interface OnProgressUpdateListener {
-            /**
-             * Called when the progress changes. The progress is in percentage between 0 and 1,
-             * where 0 means the session has not yet started and 100 means that it has finished.
-             *
-             * @param session      The session associated with the progress.
-             * @param progress The new progress ranging from 0 ~ 100 inclusive.
-             */
-            void onProgressUpdate(@NonNull TranscodingSession session,
-                    @IntRange(from = 0, to = 100) int progress);
-        }
-
-        private final MediaTranscodingManager mManager;
-        private Executor mListenerExecutor;
-        private OnTranscodingFinishedListener mListener;
-        private int mSessionId = -1;
-        // Lock for internal state.
-        private final Object mLock = new Object();
-        @GuardedBy("mLock")
-        private Executor mProgressUpdateExecutor = null;
-        @GuardedBy("mLock")
-        private OnProgressUpdateListener mProgressUpdateListener = null;
-        @GuardedBy("mLock")
-        private int mProgress = 0;
-        @GuardedBy("mLock")
-        private int mProgressUpdateInterval = 0;
-        @GuardedBy("mLock")
-        private @Status int mStatus = STATUS_PENDING;
-        @GuardedBy("mLock")
-        private @Result int mResult = RESULT_NONE;
-        @GuardedBy("mLock")
-        private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
-        @GuardedBy("mLock")
-        private boolean mHasRetried = false;
-        // The original request that associated with this session.
-        private final TranscodingRequest mRequest;
-
-        private TranscodingSession(
-                @NonNull MediaTranscodingManager manager,
-                @NonNull TranscodingRequest request,
-                @NonNull TranscodingSessionParcel parcel,
-                @NonNull @CallbackExecutor Executor executor,
-                @NonNull OnTranscodingFinishedListener listener) {
-            Objects.requireNonNull(manager, "manager must not be null");
-            Objects.requireNonNull(parcel, "parcel must not be null");
-            Objects.requireNonNull(executor, "listenerExecutor must not be null");
-            Objects.requireNonNull(listener, "listener must not be null");
-            mManager = manager;
-            mSessionId = parcel.sessionId;
-            mListenerExecutor = executor;
-            mListener = listener;
-            mRequest = request;
-        }
-
-        /**
-         * Set a progress listener.
-         * @param executor The executor on which listener will be invoked.
-         * @param listener The progress listener.
-         */
-        public void setOnProgressUpdateListener(
-                @NonNull @CallbackExecutor Executor executor,
-                @Nullable OnProgressUpdateListener listener) {
-            synchronized (mLock) {
-                Objects.requireNonNull(executor, "listenerExecutor must not be null");
-                Objects.requireNonNull(listener, "listener must not be null");
-                mProgressUpdateExecutor = executor;
-                mProgressUpdateListener = listener;
-            }
-        }
-
-        private void updateStatusAndResult(@Status int sessionStatus,
-                @Result int sessionResult, @TranscodingSessionErrorCode int errorCode) {
-            synchronized (mLock) {
-                mStatus = sessionStatus;
-                mResult = sessionResult;
-                mErrorCode = errorCode;
-            }
-        }
-
-        /**
-         * Retrieve the error code associated with the RESULT_ERROR.
-         */
-        public @TranscodingSessionErrorCode int getErrorCode() {
-            synchronized (mLock) {
-                return mErrorCode;
-            }
-        }
-
-        /**
-         * Resubmit the transcoding session to the service.
-         * Note that only the session that fails or gets cancelled could be retried and each session
-         * could be retried only once. After that, Client need to enqueue a new request if they want
-         * to try again.
-         *
-         * @return true if successfully resubmit the job to service. False otherwise.
-         * @throws UnsupportedOperationException if the retry could not be fulfilled.
-         * @hide
-         */
-        public boolean retry() {
-            return retryInternal(true /*setHasRetried*/);
-        }
-
-        // TODO(hkuang): Add more test for it.
-        private boolean retryInternal(boolean setHasRetried) {
-            synchronized (mLock) {
-                if (mStatus == STATUS_PENDING || mStatus == STATUS_RUNNING) {
-                    throw new UnsupportedOperationException(
-                            "Failed to retry as session is in processing");
-                }
-
-                if (mHasRetried) {
-                    throw new UnsupportedOperationException("Session has been retried already");
-                }
-
-                // Get the client interface.
-                ITranscodingClient client = mManager.getTranscodingClient();
-                if (client == null) {
-                    Log.e(TAG, "Service rebooting. Try again later");
-                    return false;
-                }
-
-                synchronized (mManager.mPendingTranscodingSessions) {
-                    try {
-                        // Submits the request to MediaTranscoding service.
-                        TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel();
-                        if (!client.submitRequest(mRequest.writeToParcel(mManager.mContext),
-                                                  sessionParcel)) {
-                            mHasRetried = true;
-                            throw new UnsupportedOperationException("Failed to enqueue request");
-                        }
-
-                        // Replace the old session id wit the new one.
-                        mSessionId = sessionParcel.sessionId;
-                        // Adds the new session back into pending sessions.
-                        mManager.mPendingTranscodingSessions.put(mSessionId, this);
-                    } catch (RemoteException re) {
-                        return false;
-                    }
-                    mStatus = STATUS_PENDING;
-                    mHasRetried = setHasRetried ? true : false;
-                }
-            }
-            return true;
-        }
-
-        /**
-         * Cancels the transcoding session and notify the listener.
-         * If the session happened to finish before being canceled this call is effectively a no-op
-         * and will not update the result in that case.
-         */
-        public void cancel() {
-            synchronized (mLock) {
-                // Check if the session is finished already.
-                if (mStatus != STATUS_FINISHED) {
-                    try {
-                        ITranscodingClient client = mManager.getTranscodingClient();
-                        // The client may be gone.
-                        if (client != null) {
-                            client.cancelSession(mSessionId);
-                        }
-                    } catch (RemoteException re) {
-                        //TODO(hkuang): Find out what to do if failing to cancel the session.
-                        Log.e(TAG, "Failed to cancel the session due to exception:  " + re);
-                    }
-                    mStatus = STATUS_FINISHED;
-                    mResult = RESULT_CANCELED;
-
-                    // Notifies client the session is canceled.
-                    mListenerExecutor.execute(() -> mListener.onTranscodingFinished(this));
-                }
-            }
-        }
-
-        /**
-         * Gets the progress of the transcoding session. The progress is between 0 and 100, where 0
-         * means that the session has not yet started and 100 means that it is finished. For the
-         * cancelled session, the progress will be the last updated progress before it is cancelled.
-         * @return The progress.
-         */
-        @IntRange(from = 0, to = 100)
-        public int getProgress() {
-            synchronized (mLock) {
-                return mProgress;
-            }
-        }
-
-        /**
-         * Gets the status of the transcoding session.
-         * @return The status.
-         */
-        public @Status int getStatus() {
-            synchronized (mLock) {
-                return mStatus;
-            }
-        }
-
-        /**
-         * Adds a client uid that is also waiting for this transcoding session.
-         * <p>
-         * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could add the
-         * uid. Note that the permission check happens on the service side upon starting the
-         * transcoding. If the client does not have the permission, the transcoding will fail.
-         * @param uid  the additional client uid to be added.
-         * @return true if successfully added, false otherwise.
-         */
-        public boolean addClientUid(int uid) {
-            if (uid < 0) {
-                throw new IllegalArgumentException("Invalid Uid");
-            }
-
-            // Get the client interface.
-            ITranscodingClient client = mManager.getTranscodingClient();
-            if (client == null) {
-                Log.e(TAG, "Service is dead...");
-                return false;
-            }
-
-            try {
-                if (!client.addClientUid(mSessionId, uid)) {
-                    Log.e(TAG, "Failed to add client uid");
-                    return false;
-                }
-            } catch (Exception ex) {
-                Log.e(TAG, "Failed to get client uids due to " + ex);
-                return false;
-            }
-            return true;
-        }
-
-        /**
-         * Query all the client that waiting for this transcoding session
-         * @return a list containing all the client uids.
-         */
-        @NonNull
-        public List<Integer> getClientUids() {
-            List<Integer> uidList = new ArrayList<Integer>();
-
-            // Get the client interface.
-            ITranscodingClient client = mManager.getTranscodingClient();
-            if (client == null) {
-                Log.e(TAG, "Service is dead...");
-                return uidList;
-            }
-
-            try {
-                int[] clientUids  = client.getClientUids(mSessionId);
-                for (int i : clientUids) {
-                    uidList.add(i);
-                }
-            } catch (Exception ex) {
-                Log.e(TAG, "Failed to get client uids due to " + ex);
-            }
-
-            return uidList;
-        }
-
-        /**
-         * Gets sessionId of the transcoding session.
-         * @return session id.
-         */
-        public int getSessionId() {
-            return mSessionId;
-        }
-
-        /**
-         * Gets the result of the transcoding session.
-         * @return The result.
-         */
-        public @Result int getResult() {
-            synchronized (mLock) {
-                return mResult;
-            }
-        }
-
-        @Override
-        public String toString() {
-            String result;
-            String status;
-
-            switch (mResult) {
-                case RESULT_NONE:
-                    result = "RESULT_NONE";
-                    break;
-                case RESULT_SUCCESS:
-                    result = "RESULT_SUCCESS";
-                    break;
-                case RESULT_ERROR:
-                    result = "RESULT_ERROR(" + mErrorCode + ")";
-                    break;
-                case RESULT_CANCELED:
-                    result = "RESULT_CANCELED";
-                    break;
-                default:
-                    result = String.valueOf(mResult);
-                    break;
-            }
-
-            switch (mStatus) {
-                case STATUS_PENDING:
-                    status = "STATUS_PENDING";
-                    break;
-                case STATUS_PAUSED:
-                    status = "STATUS_PAUSED";
-                    break;
-                case STATUS_RUNNING:
-                    status = "STATUS_RUNNING";
-                    break;
-                case STATUS_FINISHED:
-                    status = "STATUS_FINISHED";
-                    break;
-                default:
-                    status = String.valueOf(mStatus);
-                    break;
-            }
-            return String.format(" session: {id: %d, status: %s, result: %s, progress: %d}",
-                    mSessionId, status, result, mProgress);
-        }
-
-        private void updateProgress(int newProgress) {
-            synchronized (mLock) {
-                mProgress = newProgress;
-            }
-        }
-
-        private void updateStatus(int newStatus) {
-            synchronized (mLock) {
-                mStatus = newStatus;
-            }
-        }
-    }
-
-    private ITranscodingClient getTranscodingClient() {
-        synchronized (mLock) {
-            return mTranscodingClient;
-        }
-    }
-
-    /**
-     * Enqueues a TranscodingRequest for execution.
-     * <p> Upon successfully accepting the request, MediaTranscodingManager will return a
-     * {@link TranscodingSession} to the client. Client should use {@link TranscodingSession} to
-     * track the progress and get the result.
-     * <p> MediaTranscodingManager will return null if fails to accept the request due to service
-     * rebooting. Client could retry again after receiving null.
-     *
-     * @param transcodingRequest The TranscodingRequest to enqueue.
-     * @param listenerExecutor   Executor on which the listener is notified.
-     * @param listener           Listener to get notified when the transcoding session is finished.
-     * @return A TranscodingSession for this operation.
-     * @throws UnsupportedOperationException if the request could not be fulfilled.
-     */
-    @Nullable
-    public TranscodingSession enqueueRequest(
-            @NonNull TranscodingRequest transcodingRequest,
-            @NonNull @CallbackExecutor Executor listenerExecutor,
-            @NonNull OnTranscodingFinishedListener listener) {
-        Log.i(TAG, "enqueueRequest called.");
-        Objects.requireNonNull(transcodingRequest, "transcodingRequest must not be null");
-        Objects.requireNonNull(listenerExecutor, "listenerExecutor must not be null");
-        Objects.requireNonNull(listener, "listener must not be null");
-
-        // Converts the request to TranscodingRequestParcel.
-        TranscodingRequestParcel requestParcel = transcodingRequest.writeToParcel(mContext);
-
-        Log.i(TAG, "Getting transcoding request " + transcodingRequest.getSourceUri());
-
-        // Submits the request to MediaTranscoding service.
-        try {
-            TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel();
-            // Synchronizes the access to mPendingTranscodingSessions to make sure the session Id is
-            // inserted in the mPendingTranscodingSessions in the callback handler.
-            synchronized (mPendingTranscodingSessions) {
-                synchronized (mLock) {
-                    if (mTranscodingClient == null) {
-                        // Try to register with the service again.
-                        IMediaTranscodingService service = getService(false /*retry*/);
-                        if (service == null) {
-                            Log.w(TAG, "Service rebooting. Try again later");
-                            return null;
-                        }
-                        mTranscodingClient = registerClient(service);
-                        // If still fails, throws an exception to tell client to try later.
-                        if (mTranscodingClient == null) {
-                            Log.w(TAG, "Service rebooting. Try again later");
-                            return null;
-                        }
-                    }
-
-                    if (!mTranscodingClient.submitRequest(requestParcel, sessionParcel)) {
-                        throw new UnsupportedOperationException("Failed to enqueue request");
-                    }
-                }
-
-                // Wraps the TranscodingSessionParcel into a TranscodingSession and returns it to
-                // client for tracking.
-                TranscodingSession session = new TranscodingSession(this, transcodingRequest,
-                        sessionParcel,
-                        listenerExecutor,
-                        listener);
-
-                // Adds the new session into pending sessions.
-                mPendingTranscodingSessions.put(session.getSessionId(), session);
-                return session;
-            }
-        } catch (RemoteException ex) {
-            Log.w(TAG, "Service rebooting. Try again later");
-            return null;
-        } catch (ServiceSpecificException ex) {
-            throw new UnsupportedOperationException(
-                    "Failed to submit request to Transcoding service. Error: " + ex);
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/ProxyDataSourceCallback.java b/apex/media/framework/java/android/media/ProxyDataSourceCallback.java
deleted file mode 100644
index 14d3ce8..0000000
--- a/apex/media/framework/java/android/media/ProxyDataSourceCallback.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 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.media;
-
-import android.os.ParcelFileDescriptor;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-/**
- * A DataSourceCallback that is backed by a ParcelFileDescriptor.
- */
-class ProxyDataSourceCallback extends DataSourceCallback {
-    private static final String TAG = "TestDataSourceCallback";
-
-    ParcelFileDescriptor mPFD;
-    FileDescriptor mFD;
-
-    ProxyDataSourceCallback(ParcelFileDescriptor pfd) throws IOException {
-        mPFD = pfd.dup();
-        mFD = mPFD.getFileDescriptor();
-    }
-
-    @Override
-    public synchronized int readAt(long position, byte[] buffer, int offset, int size)
-            throws IOException {
-        try {
-            Os.lseek(mFD, position, OsConstants.SEEK_SET);
-            int ret = Os.read(mFD, buffer, offset, size);
-            return (ret == 0) ? END_OF_STREAM : ret;
-        } catch (ErrnoException e) {
-            throw new IOException(e);
-        }
-    }
-
-    @Override
-    public synchronized long getSize() throws IOException {
-        return mPFD.getStatSize();
-    }
-
-    @Override
-    public synchronized void close() {
-        try {
-            mPFD.close();
-        } catch (IOException e) {
-            Log.e(TAG, "Failed to close the PFD.", e);
-        }
-    }
-}
-
diff --git a/apex/media/framework/java/android/media/RoutingDelegate.java b/apex/media/framework/java/android/media/RoutingDelegate.java
deleted file mode 100644
index 2359813..0000000
--- a/apex/media/framework/java/android/media/RoutingDelegate.java
+++ /dev/null
@@ -1,48 +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.media;
-
-import android.os.Handler;
-
-class RoutingDelegate implements AudioRouting.OnRoutingChangedListener {
-    private AudioRouting mAudioRouting;
-    private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener;
-    private Handler mHandler;
-
-    RoutingDelegate(final AudioRouting audioRouting,
-                    final AudioRouting.OnRoutingChangedListener listener,
-                    Handler handler) {
-        mAudioRouting = audioRouting;
-        mOnRoutingChangedListener = listener;
-        mHandler = handler;
-    }
-
-    public AudioRouting.OnRoutingChangedListener getListener() {
-        return mOnRoutingChangedListener;
-    }
-
-    public Handler getHandler() {
-        return mHandler;
-    }
-
-    @Override
-    public void onRoutingChanged(AudioRouting router) {
-        if (mOnRoutingChangedListener != null) {
-            mOnRoutingChangedListener.onRoutingChanged(mAudioRouting);
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/Session2Command.java b/apex/media/framework/java/android/media/Session2Command.java
deleted file mode 100644
index 7e71591..0000000
--- a/apex/media/framework/java/android/media/Session2Command.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import java.util.Objects;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- * <p>
- * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
- * <p>
- * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
- * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
- * {@link #getCustomAction()} shouldn't be {@code null}.
- * <p>
- * Refer to the <a href="{@docRoot}reference/androidx/media2/session/SessionCommand.html">
- * AndroidX SessionCommand</a> class for the list of valid commands.
- */
-public final class Session2Command implements Parcelable {
-    /**
-     * Command code for the custom command which can be defined by string action in the
-     * {@link Session2Command}.
-     */
-    public static final int COMMAND_CODE_CUSTOM = 0;
-
-    public static final @android.annotation.NonNull Parcelable.Creator<Session2Command> CREATOR =
-            new Parcelable.Creator<Session2Command>() {
-                @Override
-                public Session2Command createFromParcel(Parcel in) {
-                    return new Session2Command(in);
-                }
-
-                @Override
-                public Session2Command[] newArray(int size) {
-                    return new Session2Command[size];
-                }
-            };
-
-    private final int mCommandCode;
-    // Nonnull if it's custom command
-    private final String mCustomAction;
-    private final Bundle mCustomExtras;
-
-    /**
-     * Constructor for creating a command predefined in AndroidX media2.
-     *
-     * @param commandCode A command code for a command predefined in AndroidX media2.
-     */
-    public Session2Command(int commandCode) {
-        if (commandCode == COMMAND_CODE_CUSTOM) {
-            throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM");
-        }
-        mCommandCode = commandCode;
-        mCustomAction = null;
-        mCustomExtras = null;
-    }
-
-    /**
-     * Constructor for creating a custom command.
-     *
-     * @param action The action of this custom command.
-     * @param extras An extra bundle for this custom command.
-     */
-    public Session2Command(@NonNull String action, @Nullable Bundle extras) {
-        if (action == null) {
-            throw new IllegalArgumentException("action shouldn't be null");
-        }
-        mCommandCode = COMMAND_CODE_CUSTOM;
-        mCustomAction = action;
-        mCustomExtras = extras;
-    }
-
-    /**
-     * Used by parcelable creator.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    Session2Command(Parcel in) {
-        mCommandCode = in.readInt();
-        mCustomAction = in.readString();
-        mCustomExtras = in.readBundle();
-    }
-
-    /**
-     * Gets the command code of a predefined command.
-     * This will return {@link #COMMAND_CODE_CUSTOM} for a custom command.
-     */
-    public int getCommandCode() {
-        return mCommandCode;
-    }
-
-    /**
-     * Gets the action of a custom command.
-     * This will return {@code null} for a predefined command.
-     */
-    @Nullable
-    public String getCustomAction() {
-        return mCustomAction;
-    }
-
-    /**
-     * Gets the extra bundle of a custom command.
-     * This will return {@code null} for a predefined command.
-     */
-    @Nullable
-    public Bundle getCustomExtras() {
-        return mCustomExtras;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        if (dest == null) {
-            throw new IllegalArgumentException("parcel shouldn't be null");
-        }
-        dest.writeInt(mCommandCode);
-        dest.writeString(mCustomAction);
-        dest.writeBundle(mCustomExtras);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (!(obj instanceof Session2Command)) {
-            return false;
-        }
-        Session2Command other = (Session2Command) obj;
-        return mCommandCode == other.mCommandCode
-                && TextUtils.equals(mCustomAction, other.mCustomAction);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mCustomAction, mCommandCode);
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Contains the result of {@link Session2Command}.
-     */
-    public static final class Result {
-        private final int mResultCode;
-        private final Bundle mResultData;
-
-        /**
-         * Result code representing that the command is skipped or canceled. For an example, a seek
-         * command can be skipped if it is followed by another seek command.
-         */
-        public static final int RESULT_INFO_SKIPPED = 1;
-
-        /**
-         * Result code representing that the command is successfully completed.
-         */
-        public static final int RESULT_SUCCESS = 0;
-
-        /**
-         * Result code represents that call is ended with an unknown error.
-         */
-        public static final int RESULT_ERROR_UNKNOWN_ERROR = -1;
-
-        /**
-         * Constructor of {@link Result}.
-         *
-         * @param resultCode result code
-         * @param resultData result data
-         */
-        public Result(int resultCode, @Nullable Bundle resultData) {
-            mResultCode = resultCode;
-            mResultData = resultData;
-        }
-
-        /**
-         * Returns the result code.
-         */
-        public int getResultCode() {
-            return mResultCode;
-        }
-
-        /**
-         * Returns the result data.
-         */
-        @Nullable
-        public Bundle getResultData() {
-            return mResultData;
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/Session2CommandGroup.java b/apex/media/framework/java/android/media/Session2CommandGroup.java
deleted file mode 100644
index af8184a..0000000
--- a/apex/media/framework/java/android/media/Session2CommandGroup.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static android.media.Session2Command.COMMAND_CODE_CUSTOM;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- * <p>
- * A set of {@link Session2Command} which represents a command group.
- */
-public final class Session2CommandGroup implements Parcelable {
-    private static final String TAG = "Session2CommandGroup";
-
-    public static final @android.annotation.NonNull Parcelable.Creator<Session2CommandGroup>
-            CREATOR = new Parcelable.Creator<Session2CommandGroup>() {
-                @Override
-                public Session2CommandGroup createFromParcel(Parcel in) {
-                    return new Session2CommandGroup(in);
-                }
-
-                @Override
-                public Session2CommandGroup[] newArray(int size) {
-                    return new Session2CommandGroup[size];
-                }
-            };
-
-    Set<Session2Command> mCommands = new HashSet<>();
-
-    /**
-     * Creates a new Session2CommandGroup with commands copied from another object.
-     *
-     * @param commands The collection of commands to copy.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    Session2CommandGroup(@Nullable Collection<Session2Command> commands) {
-        if (commands != null) {
-            mCommands.addAll(commands);
-        }
-    }
-
-    /**
-     * Used by parcelable creator.
-     */
-    @SuppressWarnings({"WeakerAccess", "UnsafeParcelApi"}) /* synthetic access */
-    Session2CommandGroup(Parcel in) {
-        Parcelable[] commands = in.readParcelableArray(Session2Command.class.getClassLoader());
-        if (commands != null) {
-            for (Parcelable command : commands) {
-                mCommands.add((Session2Command) command);
-            }
-        }
-    }
-
-    /**
-     * Checks whether this command group has a command that matches given {@code command}.
-     *
-     * @param command A command to find. Shouldn't be {@code null}.
-     */
-    public boolean hasCommand(@NonNull Session2Command command) {
-        if (command == null) {
-            throw new IllegalArgumentException("command shouldn't be null");
-        }
-        return mCommands.contains(command);
-    }
-
-    /**
-     * Checks whether this command group has a command that matches given {@code commandCode}.
-     *
-     * @param commandCode A command code to find.
-     *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
-     */
-    public boolean hasCommand(int commandCode) {
-        if (commandCode == COMMAND_CODE_CUSTOM) {
-            throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
-        }
-        for (Session2Command command : mCommands) {
-            if (command.getCommandCode() == commandCode) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Gets all commands of this command group.
-     */
-    @NonNull
-    public Set<Session2Command> getCommands() {
-        return new HashSet<>(mCommands);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        if (dest == null) {
-            throw new IllegalArgumentException("parcel shouldn't be null");
-        }
-        dest.writeParcelableArray(mCommands.toArray(new Session2Command[0]), 0);
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Builds a {@link Session2CommandGroup} object.
-     */
-    public static final class Builder {
-        private Set<Session2Command> mCommands;
-
-        public Builder() {
-            mCommands = new HashSet<>();
-        }
-
-        /**
-         * Creates a new builder for {@link Session2CommandGroup} with commands copied from another
-         * {@link Session2CommandGroup} object.
-         * @param commandGroup
-         */
-        public Builder(@NonNull Session2CommandGroup commandGroup) {
-            if (commandGroup == null) {
-                throw new IllegalArgumentException("command group shouldn't be null");
-            }
-            mCommands = commandGroup.getCommands();
-        }
-
-        /**
-         * Adds a command to this command group.
-         *
-         * @param command A command to add. Shouldn't be {@code null}.
-         */
-        @NonNull
-        public Builder addCommand(@NonNull Session2Command command) {
-            if (command == null) {
-                throw new IllegalArgumentException("command shouldn't be null");
-            }
-            mCommands.add(command);
-            return this;
-        }
-
-        /**
-         * Removes a command from this group which matches given {@code command}.
-         *
-         * @param command A command to find. Shouldn't be {@code null}.
-         */
-        @NonNull
-        public Builder removeCommand(@NonNull Session2Command command) {
-            if (command == null) {
-                throw new IllegalArgumentException("command shouldn't be null");
-            }
-            mCommands.remove(command);
-            return this;
-        }
-
-        /**
-         * Builds {@link Session2CommandGroup}.
-         *
-         * @return a new {@link Session2CommandGroup}.
-         */
-        @NonNull
-        public Session2CommandGroup build() {
-            return new Session2CommandGroup(mCommands);
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/Session2Link.java b/apex/media/framework/java/android/media/Session2Link.java
deleted file mode 100644
index 6e550e8..0000000
--- a/apex/media/framework/java/android/media/Session2Link.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.NonNull;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.util.Log;
-
-import java.util.Objects;
-
-/**
- * Handles incoming commands from {@link MediaController2} to {@link MediaSession2}.
- * @hide
- */
-// @SystemApi
-public final class Session2Link implements Parcelable {
-    private static final String TAG = "Session2Link";
-    private static final boolean DEBUG = MediaSession2.DEBUG;
-
-    public static final @android.annotation.NonNull Parcelable.Creator<Session2Link> CREATOR =
-            new Parcelable.Creator<Session2Link>() {
-                @Override
-                public Session2Link createFromParcel(Parcel in) {
-                    return new Session2Link(in);
-                }
-
-                @Override
-                public Session2Link[] newArray(int size) {
-                    return new Session2Link[size];
-                }
-            };
-
-    private final MediaSession2 mSession;
-    private final IMediaSession2 mISession;
-
-    public Session2Link(MediaSession2 session) {
-        mSession = session;
-        mISession = new Session2Stub();
-    }
-
-    Session2Link(Parcel in) {
-        mSession = null;
-        mISession = IMediaSession2.Stub.asInterface(in.readStrongBinder());
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStrongBinder(mISession.asBinder());
-    }
-
-    @Override
-    public int hashCode() {
-        return mISession.asBinder().hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof Session2Link)) {
-            return false;
-        }
-        Session2Link other = (Session2Link) obj;
-        return Objects.equals(mISession.asBinder(), other.mISession.asBinder());
-    }
-
-    /** Link to death with mISession */
-    public void linkToDeath(@NonNull IBinder.DeathRecipient recipient, int flags) {
-        if (mISession != null) {
-            try {
-                mISession.asBinder().linkToDeath(recipient, flags);
-            } catch (RemoteException e) {
-                if (DEBUG) {
-                    Log.d(TAG, "Session died too early.", e);
-                }
-            }
-        }
-    }
-
-    /** Unlink to death with mISession */
-    public boolean unlinkToDeath(@NonNull IBinder.DeathRecipient recipient, int flags) {
-        if (mISession != null) {
-            return mISession.asBinder().unlinkToDeath(recipient, flags);
-        }
-        return true;
-    }
-
-    /** Interface method for IMediaSession2.connect */
-    public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) {
-        try {
-            mISession.connect(caller, seq, connectionRequest);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaSession2.disconnect */
-    public void disconnect(final Controller2Link caller, int seq) {
-        try {
-            mISession.disconnect(caller, seq);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaSession2.sendSessionCommand */
-    public void sendSessionCommand(final Controller2Link caller, final int seq,
-            final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
-        try {
-            mISession.sendSessionCommand(caller, seq, command, args, resultReceiver);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaSession2.sendSessionCommand */
-    public void cancelSessionCommand(final Controller2Link caller, final int seq) {
-        try {
-            mISession.cancelSessionCommand(caller, seq);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Stub implementation for IMediaSession2.connect */
-    public void onConnect(final Controller2Link caller, int pid, int uid, int seq,
-            Bundle connectionRequest) {
-        mSession.onConnect(caller, pid, uid, seq, connectionRequest);
-    }
-
-    /** Stub implementation for IMediaSession2.disconnect */
-    public void onDisconnect(final Controller2Link caller, int seq) {
-        mSession.onDisconnect(caller, seq);
-    }
-
-    /** Stub implementation for IMediaSession2.sendSessionCommand */
-    public void onSessionCommand(final Controller2Link caller, final int seq,
-            final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
-        mSession.onSessionCommand(caller, seq, command, args, resultReceiver);
-    }
-
-    /** Stub implementation for IMediaSession2.cancelSessionCommand */
-    public void onCancelCommand(final Controller2Link caller, final int seq) {
-        mSession.onCancelCommand(caller, seq);
-    }
-
-    private class Session2Stub extends IMediaSession2.Stub {
-        @Override
-        public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) {
-            if (caller == null || connectionRequest == null) {
-                return;
-            }
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Session2Link.this.onConnect(caller, pid, uid, seq, connectionRequest);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void disconnect(final Controller2Link caller, int seq) {
-            if (caller == null) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Session2Link.this.onDisconnect(caller, seq);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void sendSessionCommand(final Controller2Link caller, final int seq,
-                final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
-            if (caller == null) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void cancelSessionCommand(final Controller2Link caller, final int seq) {
-            if (caller == null) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Session2Link.this.onCancelCommand(caller, seq);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/Session2Token.java b/apex/media/framework/java/android/media/Session2Token.java
deleted file mode 100644
index aae2e1b..0000000
--- a/apex/media/framework/java/android/media/Session2Token.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- * <p>
- * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
- * If it's representing a session service, it may not be ongoing.
- * <p>
- * This may be passed to apps by the session owner to allow them to create a
- * {@link MediaController2} to communicate with the session.
- * <p>
- * It can be also obtained by {@link android.media.session.MediaSessionManager}.
- */
-public final class Session2Token implements Parcelable {
-    private static final String TAG = "Session2Token";
-
-    public static final @android.annotation.NonNull Creator<Session2Token> CREATOR =
-            new Creator<Session2Token>() {
-                @Override
-                public Session2Token createFromParcel(Parcel p) {
-                    return new Session2Token(p);
-                }
-
-                @Override
-                public Session2Token[] newArray(int size) {
-                    return new Session2Token[size];
-                }
-            };
-
-    /**
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE})
-    public @interface TokenType {
-    }
-
-    /**
-     * Type for {@link MediaSession2}.
-     */
-    public static final int TYPE_SESSION = 0;
-
-    /**
-     * Type for {@link MediaSession2Service}.
-     */
-    public static final int TYPE_SESSION_SERVICE = 1;
-
-    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;
-
-    /**
-     * Constructor for the token with type {@link #TYPE_SESSION_SERVICE}.
-     *
-     * @param context The context.
-     * @param serviceComponent The component name of the service.
-     */
-    public Session2Token(@NonNull Context context, @NonNull ComponentName serviceComponent) {
-        if (context == null) {
-            throw new IllegalArgumentException("context shouldn't be null");
-        }
-        if (serviceComponent == null) {
-            throw new IllegalArgumentException("serviceComponent shouldn't be null");
-        }
-
-        final PackageManager manager = context.getPackageManager();
-        final int uid = getUid(manager, serviceComponent.getPackageName());
-
-        if (!isInterfaceDeclared(manager, MediaSession2Service.SERVICE_INTERFACE,
-                serviceComponent)) {
-            Log.w(TAG, serviceComponent + " doesn't implement MediaSession2Service.");
-        }
-        mComponentName = serviceComponent;
-        mPackageName = serviceComponent.getPackageName();
-        mServiceName = serviceComponent.getClassName();
-        mUid = uid;
-        mType = TYPE_SESSION_SERVICE;
-        mSessionLink = null;
-        mExtras = Bundle.EMPTY;
-    }
-
-    Session2Token(int uid, int type, String packageName, Session2Link sessionLink,
-            @NonNull Bundle tokenExtras) {
-        mUid = uid;
-        mType = type;
-        mPackageName = packageName;
-        mServiceName = null;
-        mComponentName = null;
-        mSessionLink = sessionLink;
-        mExtras = tokenExtras;
-    }
-
-    Session2Token(Parcel in) {
-        mUid = in.readInt();
-        mType = in.readInt();
-        mPackageName = in.readString();
-        mServiceName = in.readString();
-        mSessionLink = in.readParcelable(null);
-        mComponentName = ComponentName.unflattenFromString(in.readString());
-
-        Bundle extras = in.readBundle();
-        if (extras == null) {
-            Log.w(TAG, "extras shouldn't be null.");
-            extras = Bundle.EMPTY;
-        } else if (MediaSession2.hasCustomParcelable(extras)) {
-            Log.w(TAG, "extras contain custom parcelable. Ignoring.");
-            extras = Bundle.EMPTY;
-        }
-        mExtras = extras;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mUid);
-        dest.writeInt(mType);
-        dest.writeString(mPackageName);
-        dest.writeString(mServiceName);
-        dest.writeParcelable(mSessionLink, flags);
-        dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString());
-        dest.writeBundle(mExtras);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mType, mUid, mPackageName, mServiceName, mSessionLink);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof Session2Token)) {
-            return false;
-        }
-        Session2Token other = (Session2Token) obj;
-        return mUid == other.mUid
-                && TextUtils.equals(mPackageName, other.mPackageName)
-                && 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 + " Session2Link=" + mSessionLink + "}";
-    }
-
-    /**
-     * @return uid of the session
-     */
-    public int getUid() {
-        return mUid;
-    }
-
-    /**
-     * @return package name of the session
-     */
-    @NonNull
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    /**
-     * @return service name of the session. Can be {@code null} for {@link #TYPE_SESSION}.
-     */
-    @Nullable
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    /**
-     * @return type of the token
-     * @see #TYPE_SESSION
-     * @see #TYPE_SESSION_SERVICE
-     */
-    public @TokenType int getType() {
-        return mType;
-    }
-
-    /**
-     * @return extras of the token
-     * @see MediaSession2.Builder#setExtras(Bundle)
-     */
-    @NonNull
-    public Bundle getExtras() {
-        return new Bundle(mExtras);
-    }
-
-    Session2Link getSessionLink() {
-        return mSessionLink;
-    }
-
-    private static boolean isInterfaceDeclared(PackageManager manager, String serviceInterface,
-            ComponentName serviceComponent) {
-        Intent serviceIntent = new Intent(serviceInterface);
-        // Use queryIntentServices to find services with MediaSession2Service.SERVICE_INTERFACE.
-        // We cannot use resolveService with intent specified class name, because resolveService
-        // ignores actions if Intent.setClassName() is specified.
-        serviceIntent.setPackage(serviceComponent.getPackageName());
-
-        List<ResolveInfo> list = manager.queryIntentServices(
-                serviceIntent, PackageManager.GET_META_DATA);
-        if (list != null) {
-            for (int i = 0; i < list.size(); i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                if (resolveInfo == null || resolveInfo.serviceInfo == null) {
-                    continue;
-                }
-                if (TextUtils.equals(
-                        resolveInfo.serviceInfo.name, serviceComponent.getClassName())) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private static int getUid(PackageManager manager, String packageName) {
-        try {
-            return manager.getApplicationInfo(packageName, 0).uid;
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new IllegalArgumentException("Cannot find package " + packageName);
-        }
-    }
-}
diff --git a/apex/media/framework/jni/android_media_MediaParserJNI.cpp b/apex/media/framework/jni/android_media_MediaParserJNI.cpp
deleted file mode 100644
index c81152c..0000000
--- a/apex/media/framework/jni/android_media_MediaParserJNI.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2020, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 <jni.h>
-#include <media/MediaMetrics.h>
-
-#define JNI_FUNCTION(RETURN_TYPE, NAME, ...)                                               \
-    extern "C" {                                                                           \
-    JNIEXPORT RETURN_TYPE Java_android_media_MediaParser_##NAME(JNIEnv* env, jobject thiz, \
-                                                                ##__VA_ARGS__);            \
-    }                                                                                      \
-    JNIEXPORT RETURN_TYPE Java_android_media_MediaParser_##NAME(JNIEnv* env, jobject thiz, \
-                                                                ##__VA_ARGS__)
-
-namespace {
-
-constexpr char kMediaMetricsKey[] = "mediaparser";
-
-constexpr char kAttributeLogSessionId[] = "android.media.mediaparser.logSessionId";
-constexpr char kAttributeParserName[] = "android.media.mediaparser.parserName";
-constexpr char kAttributeCreatedByName[] = "android.media.mediaparser.createdByName";
-constexpr char kAttributeParserPool[] = "android.media.mediaparser.parserPool";
-constexpr char kAttributeLastException[] = "android.media.mediaparser.lastException";
-constexpr char kAttributeResourceByteCount[] = "android.media.mediaparser.resourceByteCount";
-constexpr char kAttributeDurationMillis[] = "android.media.mediaparser.durationMillis";
-constexpr char kAttributeTrackMimeTypes[] = "android.media.mediaparser.trackMimeTypes";
-constexpr char kAttributeTrackCodecs[] = "android.media.mediaparser.trackCodecs";
-constexpr char kAttributeAlteredParameters[] = "android.media.mediaparser.alteredParameters";
-constexpr char kAttributeVideoWidth[] = "android.media.mediaparser.videoWidth";
-constexpr char kAttributeVideoHeight[] = "android.media.mediaparser.videoHeight";
-
-// Util class to handle string resource management.
-class JstringHandle {
-public:
-    JstringHandle(JNIEnv* env, jstring value) : mEnv(env), mJstringValue(value) {
-        mCstringValue = env->GetStringUTFChars(value, /* isCopy= */ nullptr);
-    }
-
-    ~JstringHandle() {
-        if (mCstringValue != nullptr) {
-            mEnv->ReleaseStringUTFChars(mJstringValue, mCstringValue);
-        }
-    }
-
-    [[nodiscard]] const char* value() const {
-        return mCstringValue != nullptr ? mCstringValue : "";
-    }
-
-    JNIEnv* mEnv;
-    jstring mJstringValue;
-    const char* mCstringValue;
-};
-
-} // namespace
-
-JNI_FUNCTION(void, nativeSubmitMetrics, jstring logSessionIdJstring, jstring parserNameJstring,
-             jboolean createdByName, jstring parserPoolJstring, jstring lastExceptionJstring,
-             jlong resourceByteCount, jlong durationMillis, jstring trackMimeTypesJstring,
-             jstring trackCodecsJstring, jstring alteredParameters, jint videoWidth,
-             jint videoHeight) {
-    mediametrics_handle_t item(mediametrics_create(kMediaMetricsKey));
-    mediametrics_setCString(item, kAttributeLogSessionId,
-                            JstringHandle(env, logSessionIdJstring).value());
-    mediametrics_setCString(item, kAttributeParserName,
-                            JstringHandle(env, parserNameJstring).value());
-    mediametrics_setInt32(item, kAttributeCreatedByName, createdByName ? 1 : 0);
-    mediametrics_setCString(item, kAttributeParserPool,
-                            JstringHandle(env, parserPoolJstring).value());
-    mediametrics_setCString(item, kAttributeLastException,
-                            JstringHandle(env, lastExceptionJstring).value());
-    mediametrics_setInt64(item, kAttributeResourceByteCount, resourceByteCount);
-    mediametrics_setInt64(item, kAttributeDurationMillis, durationMillis);
-    mediametrics_setCString(item, kAttributeTrackMimeTypes,
-                            JstringHandle(env, trackMimeTypesJstring).value());
-    mediametrics_setCString(item, kAttributeTrackCodecs,
-                            JstringHandle(env, trackCodecsJstring).value());
-    mediametrics_setCString(item, kAttributeAlteredParameters,
-                            JstringHandle(env, alteredParameters).value());
-    mediametrics_setInt32(item, kAttributeVideoWidth, videoWidth);
-    mediametrics_setInt32(item, kAttributeVideoHeight, videoHeight);
-    mediametrics_selfRecord(item);
-    mediametrics_delete(item);
-}
diff --git a/apex/media/framework/lint-baseline.xml b/apex/media/framework/lint-baseline.xml
deleted file mode 100644
index 95eea45..0000000
--- a/apex/media/framework/lint-baseline.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.2.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.2.0-dev">
-
-    <issue
-        id="DefaultLocale"
-        message="Implicitly using the default locale is a common source of bugs: Use `toLowerCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
-        errorLine1="        if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {"
-        errorLine2="                                                        ~~~~~~~~~~~">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java"
-            line="121"
-            column="57"/>
-    </issue>
-
-    <issue
-        id="DefaultLocale"
-        message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
-        errorLine1="            return String.format(&quot; session: {id: %d, status: %s, result: %s, progress: %d}&quot;,"
-        errorLine2="                   ^">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/MediaTranscodingManager.java"
-            line="1651"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="ParcelClassLoader"
-        message="Passing null here (to use the default class loader) will not work if you are restoring your own classes. Consider using for example `getClass().getClassLoader()` instead."
-        errorLine1="            Bundle out = parcel.readBundle(null);"
-        errorLine2="                                ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/MediaSession2.java"
-            line="303"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="ParcelClassLoader"
-        message="Using the default class loader will not work if you are restoring your own classes. Consider using for example `readBundle(getClass().getClassLoader())` instead."
-        errorLine1="        mCustomExtras = in.readBundle();"
-        errorLine2="                           ~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/Session2Command.java"
-            line="104"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ParcelClassLoader"
-        message="Passing null here (to use the default class loader) will not work if you are restoring your own classes. Consider using for example `getClass().getClassLoader()` instead."
-        errorLine1="        mSessionLink = in.readParcelable(null);"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/Session2Token.java"
-            line="141"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="ParcelClassLoader"
-        message="Using the default class loader will not work if you are restoring your own classes. Consider using for example `readBundle(getClass().getClassLoader())` instead."
-        errorLine1="        Bundle extras = in.readBundle();"
-        errorLine2="                           ~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/Session2Token.java"
-            line="144"
-            column="28"/>
-    </issue>
-
-</issues>
diff --git a/apex/media/framework/updatable-media-proguard.flags b/apex/media/framework/updatable-media-proguard.flags
deleted file mode 100644
index 4e7d842..0000000
--- a/apex/media/framework/updatable-media-proguard.flags
+++ /dev/null
@@ -1,2 +0,0 @@
-# Keep all symbols in android.media.
--keep class android.media.* {*;}
diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp
deleted file mode 100644
index 0e300bb..0000000
--- a/apex/media/service/Android.bp
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT 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 {
-    // See: http://go/android-license-faq
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
-    name: "service-media-s-sources",
-    srcs: [
-        "java/**/*.java",
-    ],
-    path: "java",
-    visibility: ["//visibility:private"],
-}
-
-java_sdk_library {
-    name: "service-media-s",
-    permitted_packages: [
-        "com.android.server.media",
-    ],
-    defaults: ["framework-system-server-module-defaults"],
-    srcs: [
-        ":service-media-s-sources",
-    ],
-    libs: [
-        "androidx.annotation_annotation",
-        "updatable-media",
-        "modules-annotation-minsdk",
-        "modules-utils-build",
-    ],
-    jarjar_rules: "jarjar_rules.txt",
-    sdk_version: "system_server_current",
-    min_sdk_version: "29", // TODO: We may need to bump this at some point.
-    lint: {
-        strict_updatability_linting: true,
-    },
-    apex_available: [
-        "com.android.media",
-    ],
-}
diff --git a/apex/media/service/api/current.txt b/apex/media/service/api/current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/service/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/service/api/removed.txt b/apex/media/service/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/service/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/service/api/system-server-current.txt b/apex/media/service/api/system-server-current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/service/api/system-server-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/service/api/system-server-removed.txt b/apex/media/service/api/system-server-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/service/api/system-server-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/service/jarjar_rules.txt b/apex/media/service/jarjar_rules.txt
deleted file mode 100644
index 7e37c2b..0000000
--- a/apex/media/service/jarjar_rules.txt
+++ /dev/null
@@ -1 +0,0 @@
-rule com.android.modules.** android.media.internal.@1
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
deleted file mode 100644
index 4223fa6..0000000
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ /dev/null
@@ -1,685 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.media;
-
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.os.UserHandle.ALL;
-import static android.os.UserHandle.getUserHandleForUid;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.media.IMediaCommunicationService;
-import android.media.IMediaCommunicationServiceCallback;
-import android.media.MediaController2;
-import android.media.MediaParceledListSlice;
-import android.media.Session2CommandGroup;
-import android.media.Session2Token;
-import android.media.session.MediaSessionManager;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.view.KeyEvent;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.modules.annotation.MinSdk;
-import com.android.server.SystemService;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
-/**
- * A system service that manages {@link android.media.MediaSession2} creations
- * and their ongoing media playback state.
- * @hide
- */
-@MinSdk(Build.VERSION_CODES.S)
-@RequiresApi(Build.VERSION_CODES.S)
-public class MediaCommunicationService extends SystemService {
-    private static final String TAG = "MediaCommunicationSrv";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    final Context mContext;
-
-    final Object mLock = new Object();
-    final Handler mHandler = new Handler(Looper.getMainLooper());
-
-    @GuardedBy("mLock")
-    private final SparseIntArray mFullUserIds = new SparseIntArray();
-    @GuardedBy("mLock")
-    private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<>();
-
-    final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
-    @GuardedBy("mLock")
-    final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<>();
-    final NotificationManager mNotificationManager;
-    MediaSessionManager mSessionManager;
-
-    public MediaCommunicationService(Context context) {
-        super(context);
-        mContext = context;
-        mNotificationManager = context.getSystemService(NotificationManager.class);
-    }
-
-    @Override
-    public void onStart() {
-        publishBinderService(Context.MEDIA_COMMUNICATION_SERVICE, new Stub());
-        updateUser();
-    }
-
-    @Override
-    public void onBootPhase(int phase) {
-        super.onBootPhase(phase);
-        switch (phase) {
-            // This ensures MediaSessionService is started
-            case PHASE_BOOT_COMPLETED:
-                mSessionManager = mContext.getSystemService(MediaSessionManager.class);
-                break;
-        }
-    }
-
-    @Override
-    public void onUserStarting(@NonNull TargetUser user) {
-        if (DEBUG) Log.d(TAG, "onUserStarting: " + user);
-        updateUser();
-    }
-
-    @Override
-    public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
-        if (DEBUG) Log.d(TAG, "onUserSwitching: " + to);
-        updateUser();
-    }
-
-    @Override
-    public void onUserStopped(@NonNull TargetUser targetUser) {
-        int userId = targetUser.getUserHandle().getIdentifier();
-
-        if (DEBUG) Log.d(TAG, "onUserStopped: " + userId);
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(userId);
-            if (user != null) {
-                if (user.getFullUserId() == userId) {
-                    user.destroyAllSessions();
-                    mUserRecords.remove(userId);
-                } else {
-                    user.destroySessionsForUser(userId);
-                }
-            }
-        }
-        updateUser();
-    }
-
-    @Nullable
-    CallbackRecord findCallbackRecordLocked(@Nullable IMediaCommunicationServiceCallback callback) {
-        if (callback == null) {
-            return null;
-        }
-        for (CallbackRecord record : mCallbackRecords) {
-            if (Objects.equals(callback.asBinder(), record.mCallback.asBinder())) {
-                return record;
-            }
-        }
-        return null;
-    }
-
-    ArrayList<Session2Token> getSession2TokensLocked(int userId) {
-        ArrayList<Session2Token> list = new ArrayList<>();
-        if (userId == ALL.getIdentifier()) {
-            int size = mUserRecords.size();
-            for (int i = 0; i < size; i++) {
-                list.addAll(mUserRecords.valueAt(i).getAllSession2Tokens());
-            }
-        } else {
-            FullUserRecord user = getFullUserRecordLocked(userId);
-            if (user != null) {
-                list.addAll(user.getSession2Tokens(userId));
-            }
-        }
-        return list;
-    }
-
-    private FullUserRecord getFullUserRecordLocked(int userId) {
-        int fullUserId = mFullUserIds.get(userId, -1);
-        if (fullUserId < 0) {
-            return null;
-        }
-        return mUserRecords.get(fullUserId);
-    }
-
-    private boolean hasMediaControlPermission(int pid, int uid) {
-        // Check if it's system server or has MEDIA_CONTENT_CONTROL.
-        // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
-        // check here.
-        if (uid == Process.SYSTEM_UID || mContext.checkPermission(
-                android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
-                == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        } else if (DEBUG) {
-            Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
-        }
-        return false;
-    }
-
-    private void updateUser() {
-        UserManager manager = mContext.getSystemService(UserManager.class);
-        List<UserHandle> allUsers = manager.getUserHandles(/*excludeDying=*/false);
-
-        synchronized (mLock) {
-            mFullUserIds.clear();
-            if (allUsers != null) {
-                for (UserHandle user : allUsers) {
-                    UserHandle parent = manager.getProfileParent(user);
-                    if (parent != null) {
-                        mFullUserIds.put(user.getIdentifier(), parent.getIdentifier());
-                    } else {
-                        mFullUserIds.put(user.getIdentifier(), user.getIdentifier());
-                        if (mUserRecords.get(user.getIdentifier()) == null) {
-                            mUserRecords.put(user.getIdentifier(),
-                                    new FullUserRecord(user.getIdentifier()));
-                        }
-                    }
-                }
-            }
-            // Ensure that the current full user exists.
-            int currentFullUserId = ActivityManager.getCurrentUser();
-            FullUserRecord currentFullUserRecord = mUserRecords.get(currentFullUserId);
-            if (currentFullUserRecord == null) {
-                Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
-                currentFullUserRecord = new FullUserRecord(currentFullUserId);
-                mUserRecords.put(currentFullUserId, currentFullUserRecord);
-            }
-            mFullUserIds.put(currentFullUserId, currentFullUserId);
-        }
-    }
-
-    void dispatchSession2Created(Session2Token token) {
-        synchronized (mLock) {
-            for (CallbackRecord record : mCallbackRecords) {
-                if (record.mUserId != ALL.getIdentifier()
-                        && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
-                    continue;
-                }
-                try {
-                    record.mCallback.onSession2Created(token);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to notify session2 token created " + record);
-                }
-            }
-        }
-    }
-
-    void dispatchSession2Changed(int userId) {
-        ArrayList<Session2Token> allSession2Tokens;
-        ArrayList<Session2Token> userSession2Tokens;
-
-        synchronized (mLock) {
-            allSession2Tokens = getSession2TokensLocked(ALL.getIdentifier());
-            userSession2Tokens = getSession2TokensLocked(userId);
-
-            for (CallbackRecord record : mCallbackRecords) {
-                if (record.mUserId == ALL.getIdentifier()) {
-                    try {
-                        MediaParceledListSlice<Session2Token> toSend =
-                                new MediaParceledListSlice<>(allSession2Tokens);
-                        toSend.setInlineCountLimit(0);
-                        record.mCallback.onSession2Changed(toSend);
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to notify session2 tokens changed " + record);
-                    }
-                } else if (record.mUserId == userId) {
-                    try {
-                        MediaParceledListSlice<Session2Token> toSend =
-                                new MediaParceledListSlice<>(userSession2Tokens);
-                        toSend.setInlineCountLimit(0);
-                        record.mCallback.onSession2Changed(toSend);
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to notify session2 tokens changed " + record);
-                    }
-                }
-            }
-        }
-    }
-
-    void onSessionDied(Session2Record session) {
-        if (DEBUG) {
-            Log.d(TAG, "Destroying " + session);
-        }
-        if (session.isClosed()) {
-            Log.w(TAG, "Destroying already destroyed session. Ignoring.");
-            return;
-        }
-
-        FullUserRecord user = session.getFullUser();
-        if (user != null) {
-            user.removeSession(session);
-        }
-        session.close();
-    }
-
-    void onSessionPlaybackStateChanged(Session2Record session, boolean promotePriority) {
-        FullUserRecord user = session.getFullUser();
-        if (user == null || !user.containsSession(session)) {
-            Log.d(TAG, "Unknown session changed playback state. Ignoring.");
-            return;
-        }
-        user.onPlaybackStateChanged(session, promotePriority);
-    }
-
-
-    static boolean isMediaSessionKey(int keyCode) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_MEDIA_PLAY:
-            case KeyEvent.KEYCODE_MEDIA_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-            case KeyEvent.KEYCODE_MUTE:
-            case KeyEvent.KEYCODE_HEADSETHOOK:
-            case KeyEvent.KEYCODE_MEDIA_STOP:
-            case KeyEvent.KEYCODE_MEDIA_NEXT:
-            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-            case KeyEvent.KEYCODE_MEDIA_REWIND:
-            case KeyEvent.KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-                return true;
-        }
-        return false;
-    }
-
-    private class Stub extends IMediaCommunicationService.Stub {
-        @Override
-        public void notifySession2Created(Session2Token sessionToken) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            try {
-                if (DEBUG) {
-                    Log.d(TAG, "Session2 is created " + sessionToken);
-                }
-                if (uid != sessionToken.getUid()) {
-                    throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
-                            + " but actually=" + sessionToken.getUid());
-                }
-                FullUserRecord user;
-                int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
-                synchronized (mLock) {
-                    user = getFullUserRecordLocked(userId);
-                }
-                if (user == null) {
-                    Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
-                    return;
-                }
-                user.addSession(new Session2Record(MediaCommunicationService.this,
-                        user, sessionToken, mRecordExecutor));
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        /**
-         * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
-         * permission or an enabled notification listener)
-         *
-         * @param controllerPackageName package name of the controller app
-         * @param controllerPid pid of the controller app
-         * @param controllerUid uid of the controller app
-         */
-        @Override
-        public boolean isTrusted(String controllerPackageName, int controllerPid,
-                int controllerUid) {
-            final int uid = Binder.getCallingUid();
-            final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // Don't perform check between controllerPackageName and controllerUid.
-                // When an (activity|service) runs on the another apps process by specifying
-                // android:process in the AndroidManifest.xml, then PID and UID would have the
-                // running process' information instead of the (activity|service) that has created
-                // MediaController.
-                // Note that we can use Context#getOpPackageName() instead of
-                // Context#getPackageName() for getting package name that matches with the PID/UID,
-                // but it doesn't tell which package has created the MediaController, so useless.
-                return hasMediaControlPermission(controllerPid, controllerUid)
-                        || hasEnabledNotificationListener(
-                        userId, controllerPackageName, controllerUid);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public MediaParceledListSlice getSession2Tokens(int userId) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            try {
-                // Check that they can make calls on behalf of the user and get the final user id
-                int resolvedUserId = handleIncomingUser(pid, uid, userId, null);
-                ArrayList<Session2Token> result;
-                synchronized (mLock) {
-                    result = getSession2TokensLocked(resolvedUserId);
-                }
-                MediaParceledListSlice parceledListSlice = new MediaParceledListSlice<>(result);
-                parceledListSlice.setInlineCountLimit(1);
-                return parceledListSlice;
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void dispatchMediaKeyEvent(String packageName, KeyEvent keyEvent,
-                boolean asSystemService) {
-            if (keyEvent == null || !isMediaSessionKey(keyEvent.getKeyCode())) {
-                Log.w(TAG, "Attempted to dispatch null or non-media key event.");
-                return;
-            }
-
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                //TODO: Dispatch key event to media session 2 if required
-                mSessionManager.dispatchMediaKeyEvent(keyEvent, asSystemService);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void registerCallback(IMediaCommunicationServiceCallback callback,
-                String packageName) throws RemoteException {
-            Objects.requireNonNull(callback, "callback should not be null");
-            Objects.requireNonNull(packageName, "packageName should not be null");
-
-            synchronized (mLock) {
-                if (findCallbackRecordLocked(callback) == null) {
-
-                    CallbackRecord record = new CallbackRecord(callback, packageName,
-                            Binder.getCallingUid(), Binder.getCallingPid());
-                    mCallbackRecords.add(record);
-                    try {
-                        callback.asBinder().linkToDeath(record, 0);
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to register callback", e);
-                        mCallbackRecords.remove(record);
-                    }
-                } else {
-                    Log.e(TAG, "registerCallback is called with already registered callback. "
-                            + "packageName=" + packageName);
-                }
-            }
-        }
-
-        @Override
-        public void unregisterCallback(IMediaCommunicationServiceCallback callback)
-                throws RemoteException {
-            synchronized (mLock) {
-                CallbackRecord existingRecord = findCallbackRecordLocked(callback);
-                if (existingRecord != null) {
-                    mCallbackRecords.remove(existingRecord);
-                    callback.asBinder().unlinkToDeath(existingRecord, 0);
-                } else {
-                    Log.e(TAG, "unregisterCallback is called with unregistered callback.");
-                }
-            }
-        }
-
-        private boolean hasEnabledNotificationListener(int callingUserId,
-                String controllerPackageName, int controllerUid) {
-            int controllerUserId = UserHandle.getUserHandleForUid(controllerUid).getIdentifier();
-            if (callingUserId != controllerUserId) {
-                // Enabled notification listener only works within the same user.
-                return false;
-            }
-
-            if (mNotificationManager.hasEnabledNotificationListener(controllerPackageName,
-                    UserHandle.getUserHandleForUid(controllerUid))) {
-                return true;
-            }
-            if (DEBUG) {
-                Log.d(TAG, controllerPackageName + " (uid=" + controllerUid
-                        + ") doesn't have an enabled notification listener");
-            }
-            return false;
-        }
-
-        // Handles incoming user by checking whether the caller has permission to access the
-        // given user id's information or not. Permission is not necessary if the given user id is
-        // equal to the caller's user id, but if not, the caller needs to have the
-        // INTERACT_ACROSS_USERS_FULL permission. Otherwise, a security exception will be thrown.
-        // The return value will be the given user id, unless the given user id is
-        // UserHandle.CURRENT, which will return the ActivityManager.getCurrentUser() value instead.
-        private int handleIncomingUser(int pid, int uid, int userId, String packageName) {
-            int callingUserId = UserHandle.getUserHandleForUid(uid).getIdentifier();
-            if (userId == callingUserId) {
-                return userId;
-            }
-
-            boolean canInteractAcrossUsersFull = mContext.checkPermission(
-                    INTERACT_ACROSS_USERS_FULL, pid, uid) == PackageManager.PERMISSION_GRANTED;
-            if (canInteractAcrossUsersFull) {
-                if (userId == UserHandle.CURRENT.getIdentifier()) {
-                    return ActivityManager.getCurrentUser();
-                }
-                return userId;
-            }
-
-            throw new SecurityException("Permission denied while calling from " + packageName
-                    + " with user id: " + userId + "; Need to run as either the calling user id ("
-                    + callingUserId + "), or with " + INTERACT_ACROSS_USERS_FULL + " permission");
-        }
-    }
-
-    final class CallbackRecord implements IBinder.DeathRecipient {
-        private final IMediaCommunicationServiceCallback mCallback;
-        private final String mPackageName;
-        private final int mUid;
-        private int mPid;
-        private final int mUserId;
-
-        CallbackRecord(IMediaCommunicationServiceCallback callback,
-                String packageName, int uid, int pid) {
-            mCallback = callback;
-            mPackageName = packageName;
-            mUid = uid;
-            mPid = pid;
-            mUserId = (mContext.checkPermission(
-                    INTERACT_ACROSS_USERS_FULL, pid, uid) == PackageManager.PERMISSION_GRANTED)
-                    ? ALL.getIdentifier() : UserHandle.getUserHandleForUid(mUid).getIdentifier();
-        }
-
-        @Override
-        public String toString() {
-            return "CallbackRecord[callback=" + mCallback + ", pkg=" + mPackageName
-                    + ", uid=" + mUid + ", pid=" + mPid + "]";
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mLock) {
-                mCallbackRecords.remove(this);
-            }
-        }
-    }
-
-    final class FullUserRecord {
-        private final int mFullUserId;
-        private final SessionPriorityList mSessionPriorityList = new SessionPriorityList();
-
-        FullUserRecord(int fullUserId) {
-            mFullUserId = fullUserId;
-        }
-
-        public void addSession(Session2Record record) {
-            mSessionPriorityList.addSession(record);
-            mHandler.post(() -> dispatchSession2Created(record.mSessionToken));
-            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
-        }
-
-        private void removeSession(Session2Record record) {
-            mSessionPriorityList.removeSession(record);
-            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
-            //TODO: Handle if the removed session was the media button session.
-        }
-
-        public int getFullUserId() {
-            return mFullUserId;
-        }
-
-        public List<Session2Token> getAllSession2Tokens() {
-            return mSessionPriorityList.getAllTokens();
-        }
-
-        public List<Session2Token> getSession2Tokens(int userId) {
-            return mSessionPriorityList.getTokensByUserId(userId);
-        }
-
-        public void destroyAllSessions() {
-            mSessionPriorityList.destroyAllSessions();
-            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
-        }
-
-        public void destroySessionsForUser(int userId) {
-            if (mSessionPriorityList.destroySessionsByUserId(userId)) {
-                mHandler.post(() -> dispatchSession2Changed(mFullUserId));
-            }
-        }
-
-        public boolean containsSession(Session2Record session) {
-            return mSessionPriorityList.contains(session);
-        }
-
-        public void onPlaybackStateChanged(Session2Record session, boolean promotePriority) {
-            mSessionPriorityList.onPlaybackStateChanged(session, promotePriority);
-        }
-    }
-
-    static final class Session2Record {
-        final Session2Token mSessionToken;
-        final Object mSession2RecordLock = new Object();
-        final WeakReference<MediaCommunicationService> mServiceRef;
-        final WeakReference<FullUserRecord> mFullUserRef;
-        @GuardedBy("mSession2RecordLock")
-        private final MediaController2 mController;
-
-        @GuardedBy("mSession2RecordLock")
-        boolean mIsConnected;
-        @GuardedBy("mSession2RecordLock")
-        private boolean mIsClosed;
-
-        //TODO: introduce policy (See MediaSessionPolicyProvider)
-        Session2Record(MediaCommunicationService service, FullUserRecord fullUser,
-                Session2Token token, Executor controllerExecutor) {
-            mServiceRef = new WeakReference<>(service);
-            mFullUserRef = new WeakReference<>(fullUser);
-            mSessionToken = token;
-            mController = new MediaController2.Builder(service.getContext(), token)
-                    .setControllerCallback(controllerExecutor, new Controller2Callback())
-                    .build();
-        }
-
-        public int getUserId() {
-            return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier();
-        }
-
-        public FullUserRecord getFullUser() {
-            return mFullUserRef.get();
-        }
-
-        public boolean isClosed() {
-            synchronized (mSession2RecordLock) {
-                return mIsClosed;
-            }
-        }
-
-        public void close() {
-            synchronized (mSession2RecordLock) {
-                mIsClosed = true;
-                mController.close();
-            }
-        }
-
-        public Session2Token getSessionToken() {
-            return mSessionToken;
-        }
-
-        public boolean checkPlaybackActiveState(boolean expected) {
-            synchronized (mSession2RecordLock) {
-                return mIsConnected && mController.isPlaybackActive() == expected;
-            }
-        }
-
-        private class Controller2Callback extends MediaController2.ControllerCallback {
-            @Override
-            public void onConnected(MediaController2 controller,
-                    Session2CommandGroup allowedCommands) {
-                if (DEBUG) {
-                    Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
-                }
-                synchronized (mSession2RecordLock) {
-                    mIsConnected = true;
-                }
-            }
-
-            @Override
-            public void onDisconnected(MediaController2 controller) {
-                if (DEBUG) {
-                    Log.d(TAG, "disconnected from " + mSessionToken);
-                }
-                synchronized (mSession2RecordLock) {
-                    mIsConnected = false;
-                }
-                MediaCommunicationService service = mServiceRef.get();
-                if (service != null) {
-                    service.onSessionDied(Session2Record.this);
-                }
-            }
-
-            @Override
-            public void onPlaybackActiveChanged(
-                    @NonNull MediaController2 controller,
-                    boolean playbackActive) {
-                if (DEBUG) {
-                    Log.d(TAG, "playback active changed, " + mSessionToken + ", active="
-                            + playbackActive);
-                }
-                MediaCommunicationService service = mServiceRef.get();
-                if (service != null) {
-                    service.onSessionPlaybackStateChanged(Session2Record.this, playbackActive);
-                }
-            }
-        }
-    }
-}
diff --git a/apex/media/service/java/com/android/server/media/SessionPriorityList.java b/apex/media/service/java/com/android/server/media/SessionPriorityList.java
deleted file mode 100644
index 8145861..0000000
--- a/apex/media/service/java/com/android/server/media/SessionPriorityList.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.media;
-
-import android.annotation.Nullable;
-import android.media.Session2Token;
-import android.os.Build;
-import android.util.Log;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.modules.annotation.MinSdk;
-import com.android.server.media.MediaCommunicationService.Session2Record;
-
-import java.util.ArrayList;
-import java.util.List;
-
-//TODO: Define the priority specifically.
-/**
- * Keeps track of media sessions and their priority for notifications, media
- * button dispatch, etc.
- * Higher priority session has more chance to be selected as media button session,
- * which receives the media button events.
- */
-@MinSdk(Build.VERSION_CODES.S)
-@RequiresApi(Build.VERSION_CODES.S)
-class SessionPriorityList {
-    private static final String TAG = "SessionPriorityList";
-    private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
-    private final List<Session2Record> mSessions = new ArrayList<>();
-
-    @Nullable
-    private Session2Record mMediaButtonSession;
-    @Nullable
-    private Session2Record mCachedVolumeSession;
-
-    //TODO: integrate AudioPlayerStateMonitor
-
-    public void addSession(Session2Record record) {
-        synchronized (mLock) {
-            mSessions.add(record);
-        }
-    }
-
-    public void removeSession(Session2Record record) {
-        synchronized (mLock) {
-            mSessions.remove(record);
-        }
-        if (record == mMediaButtonSession) {
-            updateMediaButtonSession(null);
-        }
-    }
-
-    public void destroyAllSessions() {
-        synchronized (mLock) {
-            for (Session2Record session : mSessions) {
-                session.close();
-            }
-            mSessions.clear();
-        }
-    }
-
-    public boolean destroySessionsByUserId(int userId) {
-        boolean changed = false;
-        synchronized (mLock) {
-            for (int i = mSessions.size() - 1; i >= 0; i--) {
-                Session2Record session = mSessions.get(i);
-                if (session.getUserId() == userId) {
-                    mSessions.remove(i);
-                    session.close();
-                    changed = true;
-                }
-            }
-        }
-        return changed;
-    }
-
-    public List<Session2Token> getAllTokens() {
-        List<Session2Token> sessions = new ArrayList<>();
-        synchronized (mLock) {
-            for (Session2Record session : mSessions) {
-                sessions.add(session.getSessionToken());
-            }
-        }
-        return sessions;
-    }
-
-    public List<Session2Token> getTokensByUserId(int userId) {
-        List<Session2Token> sessions = new ArrayList<>();
-        synchronized (mLock) {
-            for (Session2Record session : mSessions) {
-                if (session.getUserId() == userId) {
-                    sessions.add(session.getSessionToken());
-                }
-            }
-        }
-        return sessions;
-    }
-
-    /** Gets the media button session which receives the media button events. */
-    @Nullable
-    public Session2Record getMediaButtonSession() {
-        return mMediaButtonSession;
-    }
-
-    /** Gets the media volume session which receives the volume key events. */
-    @Nullable
-    public Session2Record getMediaVolumeSession() {
-        //TODO: if null, calculate it.
-        return mCachedVolumeSession;
-    }
-
-    public boolean contains(Session2Record session) {
-        synchronized (mLock) {
-            return mSessions.contains(session);
-        }
-    }
-
-    public void onPlaybackStateChanged(Session2Record session, boolean promotePriority) {
-        if (promotePriority) {
-            synchronized (mLock) {
-                if (mSessions.remove(session)) {
-                    mSessions.add(0, session);
-                } else {
-                    Log.w(TAG, "onPlaybackStateChanged: Ignoring unknown session");
-                    return;
-                }
-            }
-        } else if (session.checkPlaybackActiveState(false)) {
-            // Just clear the cached volume session when a state goes inactive
-            mCachedVolumeSession = null;
-        }
-
-        // In most cases, playback state isn't needed for finding the media button session,
-        // but we only use it as a hint if an app has multiple local media sessions.
-        // In that case, we pick the media session whose PlaybackState matches
-        // the audio playback configuration.
-        if (mMediaButtonSession != null
-                && mMediaButtonSession.getSessionToken().getUid()
-                == session.getSessionToken().getUid()) {
-            Session2Record newMediaButtonSession =
-                    findMediaButtonSession(mMediaButtonSession.getSessionToken().getUid());
-            if (newMediaButtonSession != mMediaButtonSession) {
-                // Check if the policy states that this session should not be updated as a media
-                // button session.
-                updateMediaButtonSession(newMediaButtonSession);
-            }
-        }
-    }
-
-    private void updateMediaButtonSession(@Nullable Session2Record newSession) {
-        mMediaButtonSession = newSession;
-        //TODO: invoke callbacks for media button session changed listeners
-    }
-
-    /**
-     * Finds the media button session with the given {@param uid}.
-     * If the app has multiple media sessions, the media session whose playback state is not null
-     * and matches the audio playback state becomes the media button session. Otherwise the top
-     * priority session becomes the media button session.
-     *
-     * @return The media button session. Returns {@code null} if the app doesn't have a media
-     *   session.
-     */
-    @Nullable
-    private Session2Record findMediaButtonSession(int uid) {
-        Session2Record mediaButtonSession = null;
-        synchronized (mLock) {
-            for (Session2Record session : mSessions) {
-                if (uid != session.getSessionToken().getUid()) {
-                    continue;
-                }
-                // TODO: check audio player state monitor
-                if (mediaButtonSession == null) {
-                    // Pick the top priority session as a default.
-                    mediaButtonSession = session;
-                }
-            }
-        }
-        return mediaButtonSession;
-    }
-}
diff --git a/apex/media/service/lint-baseline.xml b/apex/media/service/lint-baseline.xml
deleted file mode 100644
index def6baf..0000000
--- a/apex/media/service/lint-baseline.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.2.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.2.0-dev">
-
-</issues>
diff --git a/core/api/current.txt b/core/api/current.txt
index bf49838..139ff3e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4550,6 +4550,7 @@
     method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
     method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int);
+    method @NonNull public static android.app.ActivityOptions makeLaunchIntoPip(@NonNull android.app.PictureInPictureParams);
     method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.view.View, String);
     method @java.lang.SafeVarargs public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.util.Pair<android.view.View,java.lang.String>...);
@@ -6635,6 +6636,7 @@
 
   public static class PictureInPictureParams.Builder {
     ctor public PictureInPictureParams.Builder();
+    ctor public PictureInPictureParams.Builder(@NonNull android.app.PictureInPictureParams);
     method public android.app.PictureInPictureParams build();
     method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
     method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
@@ -16995,6 +16997,7 @@
 
   public class SensorEvent {
     field public int accuracy;
+    field public boolean firstEventAfterDiscontinuity;
     field public android.hardware.Sensor sensor;
     field public long timestamp;
     field public final float[] values;
@@ -17006,7 +17009,6 @@
     method public void onFlushCompleted(android.hardware.Sensor);
     method public void onSensorAdditionalInfo(android.hardware.SensorAdditionalInfo);
     method public void onSensorChanged(android.hardware.SensorEvent);
-    method public void onSensorDiscontinuity(@NonNull android.hardware.Sensor);
   }
 
   public interface SensorEventListener {
@@ -17124,6 +17126,9 @@
 
   public final class SensorPrivacyManager {
     method public boolean supportsSensorToggle(int);
+    method public boolean supportsSensorToggle(int, int);
+    field public static final int TOGGLE_TYPE_HARDWARE = 2; // 0x2
+    field public static final int TOGGLE_TYPE_SOFTWARE = 1; // 0x1
   }
 
   public static class SensorPrivacyManager.Sensors {
@@ -39511,7 +39516,7 @@
     method public void onCheckRecognitionSupport(@NonNull android.content.Intent, @NonNull android.speech.RecognitionService.SupportCallback);
     method protected abstract void onStartListening(android.content.Intent, android.speech.RecognitionService.Callback);
     method protected abstract void onStopListening(android.speech.RecognitionService.Callback);
-    method public void triggerModelDownload(@NonNull android.content.Intent);
+    method public void onTriggerModelDownload(@NonNull android.content.Intent);
     field public static final String SERVICE_INTERFACE = "android.speech.RecognitionService";
     field public static final String SERVICE_META_DATA = "android.speech";
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5d76b08..785ad13 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2254,13 +2254,20 @@
 
   public class BaseTemplateData implements android.os.Parcelable {
     method public int describeContents();
+    method public int getLayoutWeight();
+    method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getPrimaryLoggingInfo();
     method @Nullable public android.app.smartspace.uitemplatedata.TapAction getPrimaryTapAction();
     method @Nullable public android.app.smartspace.uitemplatedata.Icon getSubtitleIcon();
     method @Nullable public android.app.smartspace.uitemplatedata.Text getSubtitleText();
     method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalAlarmText();
+    method @Nullable public android.app.smartspace.uitemplatedata.Icon getSupplementalIcon();
+    method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getSupplementalLoggingInfo();
     method @Nullable public android.app.smartspace.uitemplatedata.Icon getSupplementalSubtitleIcon();
+    method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getSupplementalSubtitleLoggingInfo();
     method @Nullable public android.app.smartspace.uitemplatedata.TapAction getSupplementalSubtitleTapAction();
     method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalSubtitleText();
+    method @Nullable public android.app.smartspace.uitemplatedata.TapAction getSupplementalTapAction();
+    method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalText();
     method public int getTemplateType();
     method @Nullable public android.app.smartspace.uitemplatedata.Icon getTitleIcon();
     method @Nullable public android.app.smartspace.uitemplatedata.Text getTitleText();
@@ -2271,17 +2278,37 @@
   public static class BaseTemplateData.Builder {
     ctor public BaseTemplateData.Builder(int);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData build();
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setLayoutWeight(int);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setPrimaryLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setPrimaryTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleText(@NonNull android.app.smartspace.uitemplatedata.Text);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalAlarmText(@NonNull android.app.smartspace.uitemplatedata.Text);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleText(@NonNull android.app.smartspace.uitemplatedata.Text);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalText(@NonNull android.app.smartspace.uitemplatedata.Text);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setTitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setTitleText(@NonNull android.app.smartspace.uitemplatedata.Text);
   }
 
+  public static final class BaseTemplateData.SubItemLoggingInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getFeatureType();
+    method public int getInstanceId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo> CREATOR;
+  }
+
+  public static final class BaseTemplateData.SubItemLoggingInfo.Builder {
+    ctor public BaseTemplateData.SubItemLoggingInfo.Builder(int, int);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo build();
+  }
+
   public final class CarouselTemplateData extends android.app.smartspace.uitemplatedata.BaseTemplateData {
     method @Nullable public android.app.smartspace.uitemplatedata.TapAction getCarouselAction();
     method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem> getCarouselItems();
@@ -2408,6 +2435,7 @@
     method @Nullable public android.content.Intent getIntent();
     method @Nullable public android.app.PendingIntent getPendingIntent();
     method @Nullable public android.os.UserHandle getUserHandle();
+    method public boolean shouldShowOnLockscreen();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.TapAction> CREATOR;
   }
@@ -2418,6 +2446,7 @@
     method @NonNull public android.app.smartspace.uitemplatedata.TapAction.Builder setExtras(@NonNull android.os.Bundle);
     method @NonNull public android.app.smartspace.uitemplatedata.TapAction.Builder setIntent(@NonNull android.content.Intent);
     method @NonNull public android.app.smartspace.uitemplatedata.TapAction.Builder setPendingIntent(@NonNull android.app.PendingIntent);
+    method @NonNull public android.app.smartspace.uitemplatedata.TapAction.Builder setShouldShowOnLockscreen(@NonNull boolean);
     method @NonNull public android.app.smartspace.uitemplatedata.TapAction.Builder setUserHandle(@Nullable android.os.UserHandle);
   }
 
@@ -2432,7 +2461,6 @@
 
   public static final class Text.Builder {
     ctor public Text.Builder(@NonNull CharSequence);
-    ctor public Text.Builder(@NonNull CharSequence, @NonNull android.text.TextUtils.TruncateAt);
     method @NonNull public android.app.smartspace.uitemplatedata.Text build();
     method @NonNull public android.app.smartspace.uitemplatedata.Text.Builder setMaxLines(int);
     method @NonNull public android.app.smartspace.uitemplatedata.Text.Builder setTruncateAtType(@NonNull android.text.TextUtils.TruncateAt);
@@ -3731,13 +3759,25 @@
   public final class SensorPrivacyManager {
     method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
-    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean areAnySensorPrivacyTogglesEnabled(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int, int);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
     method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean);
   }
 
   public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
-    method public void onSensorPrivacyChanged(int, boolean);
+    method public default void onSensorPrivacyChanged(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams);
+    method @Deprecated public void onSensorPrivacyChanged(int, boolean);
+  }
+
+  public static class SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams {
+    method public int getSensor();
+    method public int getToggleType();
+    method public boolean isEnabled();
   }
 
 }
@@ -5850,8 +5890,8 @@
     method @IntRange(from=0, to=1023) public int getIssueOfDataClock();
     method @IntRange(from=0, to=255) public int getIssueOfDataEphemeris();
     method @Nullable public android.location.SatellitePvt.PositionEcef getPositionEcef();
-    method @IntRange(from=0, to=604784) public int getTimeOfClock();
-    method @IntRange(from=0, to=604784) public int getTimeOfEphemeris();
+    method @IntRange(from=0) public long getTimeOfClock();
+    method @IntRange(from=0) public long getTimeOfEphemeris();
     method @FloatRange public double getTropoDelayMeters();
     method @Nullable public android.location.SatellitePvt.VelocityEcef getVelocityEcef();
     method public boolean hasIono();
@@ -5878,8 +5918,8 @@
     method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataClock(@IntRange(from=0, to=1023) int);
     method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataEphemeris(@IntRange(from=0, to=255) int);
     method @NonNull public android.location.SatellitePvt.Builder setPositionEcef(@NonNull android.location.SatellitePvt.PositionEcef);
-    method @NonNull public android.location.SatellitePvt.Builder setTimeOfClock(@IntRange(from=0, to=604784) int);
-    method @NonNull public android.location.SatellitePvt.Builder setTimeOfEphemeris(@IntRange(from=0, to=604784) int);
+    method @NonNull public android.location.SatellitePvt.Builder setTimeOfClock(@IntRange(from=0) long);
+    method @NonNull public android.location.SatellitePvt.Builder setTimeOfEphemeris(@IntRange(from=0) int);
     method @NonNull public android.location.SatellitePvt.Builder setTropoDelayMeters(@FloatRange(from=0.0f, to=100.0f) double);
     method @NonNull public android.location.SatellitePvt.Builder setVelocityEcef(@NonNull android.location.SatellitePvt.VelocityEcef);
   }
@@ -10848,7 +10888,7 @@
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onCancelAttentionCheck(@NonNull android.service.attention.AttentionService.AttentionCallback);
     method public abstract void onCheckAttention(@NonNull android.service.attention.AttentionService.AttentionCallback);
-    method public void onStartProximityUpdates(@NonNull android.service.attention.AttentionService.ProximityCallback);
+    method public void onStartProximityUpdates(@NonNull android.service.attention.AttentionService.ProximityUpdateCallback);
     method public void onStopProximityUpdates();
     field public static final int ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6; // 0x6
     field public static final int ATTENTION_FAILURE_CANCELLED = 3; // 0x3
@@ -10866,7 +10906,7 @@
     method public void onSuccess(int, long);
   }
 
-  public static final class AttentionService.ProximityCallback {
+  public static final class AttentionService.ProximityUpdateCallback {
     method public void onProximityUpdate(double);
   }
 
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 5012121..5ddaa80 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -350,6 +350,10 @@
     /** See {@link #setTransientLaunch()}. */
     private static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch";
 
+    /** see {@link #makeLaunchIntoPip(PictureInPictureParams)}. */
+    private static final String KEY_LAUNCH_INTO_PIP_PARAMS =
+            "android.activity.launchIntoPipParams";
+
     /**
      * @see #setLaunchCookie
      * @hide
@@ -444,6 +448,7 @@
     private boolean mRemoveWithTaskOrganizer;
     private boolean mLaunchedFromBubble;
     private boolean mTransientLaunch;
+    private PictureInPictureParams mLaunchIntoPipParams;
 
     /**
      * Create an ActivityOptions specifying a custom animation to run when
@@ -1106,6 +1111,24 @@
         return opts;
     }
 
+    /**
+     * Creates an {@link ActivityOptions} instance that launch into picture-in-picture.
+     * This is normally used by a Host activity to start another activity that will directly enter
+     * picture-in-picture upon its creation.
+     * @param pictureInPictureParams {@link PictureInPictureParams} for launching the Activity to
+     *                               picture-in-picture mode.
+     */
+    @NonNull
+    public static ActivityOptions makeLaunchIntoPip(
+            @NonNull PictureInPictureParams pictureInPictureParams) {
+        final ActivityOptions opts = new ActivityOptions();
+        opts.mLaunchIntoPipParams = new PictureInPictureParams.Builder(pictureInPictureParams)
+                .setIsLaunchIntoPip(true)
+                .build();
+        opts.mLaunchBounds = new Rect(pictureInPictureParams.getSourceRectHint());
+        return opts;
+    }
+
     /** @hide */
     public boolean getLaunchTaskBehind() {
         return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
@@ -1219,6 +1242,7 @@
         mLaunchedFromBubble = opts.getBoolean(KEY_LAUNCHED_FROM_BUBBLE);
         mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
         mSplashScreenStyle = opts.getInt(KEY_SPLASH_SCREEN_STYLE);
+        mLaunchIntoPipParams = opts.getParcelable(KEY_LAUNCH_INTO_PIP_PARAMS);
     }
 
     /**
@@ -1556,6 +1580,23 @@
         mLaunchWindowingMode = windowingMode;
     }
 
+    /**
+     * @return {@link PictureInPictureParams} used to launch into PiP mode.
+     * @hide
+     */
+    public PictureInPictureParams getLaunchIntoPipParams() {
+        return mLaunchIntoPipParams;
+    }
+
+    /**
+     * @return {@code true} if this instance is used to launch into PiP mode.
+     * @hide
+     */
+    public boolean isLaunchIntoPip() {
+        return mLaunchIntoPipParams != null
+                && mLaunchIntoPipParams.isLaunchIntoPip();
+    }
+
     /** @hide */
     public int getLaunchActivityType() {
         return mLaunchActivityType;
@@ -1867,6 +1908,7 @@
         mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
         mSpecsFuture = otherOptions.mSpecsFuture;
         mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
+        mLaunchIntoPipParams = otherOptions.mLaunchIntoPipParams;
     }
 
     /**
@@ -2039,6 +2081,9 @@
         if (mSplashScreenStyle != 0) {
             b.putInt(KEY_SPLASH_SCREEN_STYLE, mSplashScreenStyle);
         }
+        if (mLaunchIntoPipParams != null) {
+            b.putParcelable(KEY_LAUNCH_INTO_PIP_PARAMS, mLaunchIntoPipParams);
+        }
         return b;
     }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 0d1bc05..7c7c7ef 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2687,7 +2687,7 @@
             AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS
             AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS
             AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS
-            getSystemAlertWindowDefault(), // SYSTEM_ALERT_WINDOW
+            AppOpsManager.MODE_DEFAULT, // SYSTEM_ALERT_WINDOW /*Overridden in opToDefaultMode()*/
             AppOpsManager.MODE_ALLOWED, // ACCESS_NOTIFICATIONS
             AppOpsManager.MODE_ALLOWED, // CAMERA
             AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO
@@ -3011,6 +3011,8 @@
     private static final String DEBUG_LOGGING_OPS_PROP = "appops.logging_ops";
     private static final String DEBUG_LOGGING_TAG = "AppOpsManager";
 
+    private static volatile Integer sOpSystemAlertWindowDefaultMode;
+
     /**
      * Retrieve the op switch that controls the given operation.
      * @hide
@@ -3109,6 +3111,9 @@
      * @hide
      */
     public static @Mode int opToDefaultMode(int op) {
+        if (op == OP_SYSTEM_ALERT_WINDOW) {
+            return getSystemAlertWindowDefault();
+        }
         return sOpDefaultMode[op];
     }
 
@@ -10113,6 +10118,11 @@
     }
 
     private static int getSystemAlertWindowDefault() {
+        // This is indeed racy but we aren't expecting the result to change so it's not worth
+        // the synchronization.
+        if (sOpSystemAlertWindowDefaultMode != null) {
+            return sOpSystemAlertWindowDefaultMode;
+        }
         final Context context = ActivityThread.currentApplication();
         if (context == null) {
             return AppOpsManager.MODE_DEFAULT;
@@ -10123,10 +10133,11 @@
         // TVs are constantly plugged in and has less concern for memory/power
         if (ActivityManager.isLowRamDeviceStatic()
                 && !pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK, 0)) {
-            return AppOpsManager.MODE_IGNORED;
+            sOpSystemAlertWindowDefaultMode = AppOpsManager.MODE_IGNORED;
+        } else {
+            sOpSystemAlertWindowDefaultMode = AppOpsManager.MODE_DEFAULT;
         }
-
-        return AppOpsManager.MODE_DEFAULT;
+        return sOpSystemAlertWindowDefaultMode;
     }
 
     /**
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 5ecddfd..2d2788c 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -64,6 +64,27 @@
 
         private CharSequence mSubtitle;
 
+        private Boolean mIsLaunchIntoPip;
+
+        /** Default constructor */
+        public Builder() {}
+
+        /**
+         * Copy constructor
+         * @param original {@link PictureInPictureParams} instance this builder is built upon.
+         */
+        public Builder(@NonNull PictureInPictureParams original) {
+            mAspectRatio = original.mAspectRatio;
+            mUserActions = original.mUserActions;
+            mCloseAction = original.mCloseAction;
+            mSourceRectHint = original.mSourceRectHint;
+            mAutoEnterEnabled = original.mAutoEnterEnabled;
+            mSeamlessResizeEnabled = original.mSeamlessResizeEnabled;
+            mTitle = original.mTitle;
+            mSubtitle = original.mSubtitle;
+            mIsLaunchIntoPip = original.mIsLaunchIntoPip;
+        }
+
         /**
          * Sets the aspect ratio.  This aspect ratio is defined as the desired width / height, and
          * does not change upon device rotation.
@@ -221,6 +242,20 @@
             return this;
         }
 
+        /**
+         * Sets whether the built {@link PictureInPictureParams} represents a launch into
+         * picture-in-picture request.
+         *
+         * This property is {@code false} by default.
+         * @param isLaunchIntoPip {@code true} if the built instance represents a launch into
+         *                                 picture-in-picture request
+         * @return this builder instance.
+         */
+        @NonNull
+        Builder setIsLaunchIntoPip(boolean isLaunchIntoPip) {
+            mIsLaunchIntoPip = isLaunchIntoPip;
+            return this;
+        }
 
         /**
          * @return an immutable {@link PictureInPictureParams} to be used when entering or updating
@@ -232,7 +267,8 @@
         public PictureInPictureParams build() {
             PictureInPictureParams params = new PictureInPictureParams(mAspectRatio,
                     mExpandedAspectRatio, mUserActions, mCloseAction, mSourceRectHint,
-                    mAutoEnterEnabled, mSeamlessResizeEnabled, mTitle, mSubtitle);
+                    mAutoEnterEnabled, mSeamlessResizeEnabled, mTitle, mSubtitle,
+                    mIsLaunchIntoPip);
             return params;
         }
     }
@@ -294,6 +330,13 @@
     @Nullable
     private CharSequence mSubtitle;
 
+    /**
+     * Whether this {@link PictureInPictureParams} represents a launch into
+     * picture-in-picture request.
+     * {@link #isLaunchIntoPip()} defaults to {@code false} is this is not set.
+     */
+    private Boolean mIsLaunchIntoPip;
+
     /** {@hide} */
     PictureInPictureParams() {
     }
@@ -322,13 +365,16 @@
         if (in.readInt() != 0) {
             mSubtitle = in.readCharSequence();
         }
+        if (in.readInt() != 0) {
+            mIsLaunchIntoPip = in.readBoolean();
+        }
     }
 
     /** {@hide} */
     PictureInPictureParams(Rational aspectRatio, Rational expandedAspectRatio,
             List<RemoteAction> actions, RemoteAction closeAction, Rect sourceRectHint,
             Boolean autoEnterEnabled, Boolean seamlessResizeEnabled, CharSequence title,
-            CharSequence subtitle) {
+            CharSequence subtitle, Boolean isLaunchIntoPip) {
         mAspectRatio = aspectRatio;
         mExpandedAspectRatio = expandedAspectRatio;
         mUserActions = actions;
@@ -338,6 +384,7 @@
         mSeamlessResizeEnabled = seamlessResizeEnabled;
         mTitle = title;
         mSubtitle = subtitle;
+        mIsLaunchIntoPip = isLaunchIntoPip;
     }
 
     /**
@@ -347,8 +394,8 @@
     public PictureInPictureParams(PictureInPictureParams other) {
         this(other.mAspectRatio, other.mExpandedAspectRatio, other.mUserActions, other.mCloseAction,
                 other.hasSourceBoundsHint() ? new Rect(other.getSourceRectHint()) : null,
-                other.mAutoEnterEnabled, other.mSeamlessResizeEnabled, other.mTitle,
-                other.mSubtitle);
+                other.mAutoEnterEnabled, other.mSeamlessResizeEnabled,
+                other.mTitle, other.mSubtitle, other.mIsLaunchIntoPip);
     }
 
     /**
@@ -384,6 +431,9 @@
         if (otherArgs.hasSetSubtitle()) {
             mSubtitle = otherArgs.mSubtitle;
         }
+        if (otherArgs.mIsLaunchIntoPip != null) {
+            mIsLaunchIntoPip = otherArgs.mIsLaunchIntoPip;
+        }
     }
 
     /**
@@ -548,14 +598,22 @@
     }
 
     /**
+     * @return whether this {@link PictureInPictureParams} represents a launch into pip request.
+     * @hide
+     */
+    public boolean isLaunchIntoPip() {
+        return mIsLaunchIntoPip == null ? false : mIsLaunchIntoPip;
+    }
+
+    /**
      * @return True if no parameters are set
      * @hide
      */
     public boolean empty() {
         return !hasSourceBoundsHint() && !hasSetActions() && !hasSetCloseAction()
-                && !hasSetAspectRatio() && !hasSetExpandedAspectRatio() && mAutoEnterEnabled != null
-                && mSeamlessResizeEnabled != null && !hasSetTitle()
-                && !hasSetSubtitle();
+                && !hasSetAspectRatio() && !hasSetExpandedAspectRatio() && mAutoEnterEnabled == null
+                && mSeamlessResizeEnabled == null && !hasSetTitle()
+                && !hasSetSubtitle() && mIsLaunchIntoPip == null;
     }
 
     @Override
@@ -571,13 +629,15 @@
                 && Objects.equals(mCloseAction, that.mCloseAction)
                 && Objects.equals(mSourceRectHint, that.mSourceRectHint)
                 && Objects.equals(mTitle, that.mTitle)
-                && Objects.equals(mSubtitle, that.mSubtitle);
+                && Objects.equals(mSubtitle, that.mSubtitle)
+                && Objects.equals(mIsLaunchIntoPip, that.mIsLaunchIntoPip);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mAspectRatio, mExpandedAspectRatio, mUserActions, mCloseAction,
-                mSourceRectHint, mAutoEnterEnabled, mSeamlessResizeEnabled, mTitle, mSubtitle);
+                mSourceRectHint, mAutoEnterEnabled, mSeamlessResizeEnabled, mTitle, mSubtitle,
+                mIsLaunchIntoPip);
     }
 
     @Override
@@ -628,6 +688,12 @@
         } else {
             out.writeInt(0);
         }
+        if (mIsLaunchIntoPip != null) {
+            out.writeInt(1);
+            out.writeBoolean(mIsLaunchIntoPip);
+        } else {
+            out.writeInt(0);
+        }
     }
 
     private void writeRationalToParcel(Rational rational, Parcel out) {
@@ -659,6 +725,7 @@
                 + " isSeamlessResizeEnabled=" + isSeamlessResizeEnabled()
                 + " title=" + getTitle()
                 + " subtitle=" + getSubtitle()
+                + " isLaunchIntoPip=" + isLaunchIntoPip()
                 + ")";
     }
 
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index eca4170..5c7c73c 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -191,6 +191,13 @@
     public boolean preferDockBigOverlays;
 
     /**
+     * The task id of the host Task of the launch-into-pip Activity, i.e., it points to the Task
+     * the launch-into-pip Activity is originated from.
+     * @hide
+     */
+    public int launchIntoPipHostTaskId;
+
+    /**
      * The {@link Rect} copied from {@link DisplayCutout#getSafeInsets()} if the cutout is not of
      * (LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS),
      * {@code null} otherwise.
@@ -516,6 +523,7 @@
         topActivityType = source.readInt();
         pictureInPictureParams = source.readTypedObject(PictureInPictureParams.CREATOR);
         preferDockBigOverlays = source.readBoolean();
+        launchIntoPipHostTaskId = source.readInt();
         displayCutoutInsets = source.readTypedObject(Rect.CREATOR);
         topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
         isResizeable = source.readBoolean();
@@ -562,6 +570,7 @@
         dest.writeInt(topActivityType);
         dest.writeTypedObject(pictureInPictureParams, flags);
         dest.writeBoolean(preferDockBigOverlays);
+        dest.writeInt(launchIntoPipHostTaskId);
         dest.writeTypedObject(displayCutoutInsets, flags);
         dest.writeTypedObject(topActivityInfo, flags);
         dest.writeBoolean(isResizeable);
@@ -602,6 +611,7 @@
                 + " topActivityType=" + topActivityType
                 + " pictureInPictureParams=" + pictureInPictureParams
                 + " preferDockBigOverlays=" + preferDockBigOverlays
+                + " launchIntoPipHostTaskId=" + launchIntoPipHostTaskId
                 + " displayCutoutSafeInsets=" + displayCutoutInsets
                 + " topActivityInfo=" + topActivityInfo
                 + " launchCookies=" + launchCookies
diff --git a/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java
index a07af68..584b176 100644
--- a/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java
@@ -33,6 +33,9 @@
  *     <li> title_text (may contain a start drawable) </li>
  *     <li> subtitle_text (may contain a start drawable) . supplemental_subtitle_text (may
  *     contain a start drawable) </li>
+ *
+ *     <li> supplemental_text (contain a start drawable) . do_not_disturb_view </li>
+ *     Or
  *     <li> next_alarm_text (contain a start drawable) + supplemental_alarm_text .
  *     do_not_disturb_view </li>
  * </ul>
@@ -77,6 +80,14 @@
     private final TapAction mPrimaryTapAction;
 
     /**
+     * Primary logging info for the entire card. This will only be used when rendering a sub card
+     * within the base card. For the base card itself, BcSmartspaceCardLoggingInfo should be used,
+     * which has the display-specific info (e.g. display surface).
+     */
+    @Nullable
+    private final SubItemLoggingInfo mPrimaryLoggingInfo;
+
+    /**
      * Supplemental subtitle text and icon are shown at the second row following the subtitle text.
      * Mainly used for weather info on non-weather card.
      */
@@ -87,19 +98,47 @@
     private final Icon mSupplementalSubtitleIcon;
 
     /**
-     * Tap action for the supplemental subtitle's text and icon. Will use the primary tap action if
+     * Tap action for the supplemental subtitle's text and icon. Uses the primary tap action if
      * not being set.
      */
     @Nullable
     private final TapAction mSupplementalSubtitleTapAction;
 
     /**
+     * Logging info for the supplemental subtitle's are. Uses the primary logging info if not being
+     * set.
+     */
+    @Nullable
+    private final SubItemLoggingInfo mSupplementalSubtitleLoggingInfo;
+
+    @Nullable
+    private final Text mSupplementalText;
+
+    @Nullable
+    private final Icon mSupplementalIcon;
+
+    @Nullable
+    private final TapAction mSupplementalTapAction;
+
+    /**
+     * Logging info for the supplemental line. Uses the primary logging info if not being set.
+     */
+    @Nullable
+    private final SubItemLoggingInfo mSupplementalLoggingInfo;
+
+    /**
      * Supplemental alarm text is specifically used for holiday alarm, which is appended to "next
      * alarm".
      */
     @Nullable
     private final Text mSupplementalAlarmText;
 
+    /**
+     * The layout weight info for the card, which indicates how much space it should occupy on the
+     * screen. Default weight is 0.
+     */
+    private final int mLayoutWeight;
+
     BaseTemplateData(@NonNull Parcel in) {
         mTemplateType = in.readInt();
         mTitleText = in.readTypedObject(Text.CREATOR);
@@ -107,10 +146,17 @@
         mSubtitleText = in.readTypedObject(Text.CREATOR);
         mSubtitleIcon = in.readTypedObject(Icon.CREATOR);
         mPrimaryTapAction = in.readTypedObject(TapAction.CREATOR);
+        mPrimaryLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR);
         mSupplementalSubtitleText = in.readTypedObject(Text.CREATOR);
         mSupplementalSubtitleIcon = in.readTypedObject(Icon.CREATOR);
         mSupplementalSubtitleTapAction = in.readTypedObject(TapAction.CREATOR);
+        mSupplementalSubtitleLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR);
+        mSupplementalText = in.readTypedObject(Text.CREATOR);
+        mSupplementalIcon = in.readTypedObject(Icon.CREATOR);
+        mSupplementalTapAction = in.readTypedObject(TapAction.CREATOR);
+        mSupplementalLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR);
         mSupplementalAlarmText = in.readTypedObject(Text.CREATOR);
+        mLayoutWeight = in.readInt();
     }
 
     /**
@@ -123,20 +169,34 @@
             @Nullable Text subtitleText,
             @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
-            @Nullable Text supplementalAlarmText) {
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
+            @Nullable Text supplementalAlarmText,
+            int layoutWeight) {
         mTemplateType = templateType;
         mTitleText = titleText;
         mTitleIcon = titleIcon;
         mSubtitleText = subtitleText;
         mSubtitleIcon = subtitleIcon;
         mPrimaryTapAction = primaryTapAction;
+        mPrimaryLoggingInfo = primaryLoggingInfo;
         mSupplementalSubtitleText = supplementalSubtitleText;
         mSupplementalSubtitleIcon = supplementalSubtitleIcon;
         mSupplementalSubtitleTapAction = supplementalSubtitleTapAction;
+        mSupplementalSubtitleLoggingInfo = supplementalSubtitleLoggingInfo;
+        mSupplementalText = supplementalText;
+        mSupplementalIcon = supplementalIcon;
+        mSupplementalTapAction = supplementalTapAction;
+        mSupplementalLoggingInfo = supplementalLoggingInfo;
         mSupplementalAlarmText = supplementalAlarmText;
+        mLayoutWeight = layoutWeight;
     }
 
     /** Returns the template type. By default is UNDEFINED. */
@@ -175,6 +235,12 @@
         return mPrimaryTapAction;
     }
 
+    /** Returns the card's primary logging info. */
+    @Nullable
+    public SubItemLoggingInfo getPrimaryLoggingInfo() {
+        return mPrimaryLoggingInfo;
+    }
+
     /** Returns the supplemental subtitle's text. */
     @Nullable
     public Text getSupplementalSubtitleText() {
@@ -193,12 +259,47 @@
         return mSupplementalSubtitleTapAction;
     }
 
+    /** Returns the card's supplemental title's logging info. */
+    @Nullable
+    public SubItemLoggingInfo getSupplementalSubtitleLoggingInfo() {
+        return mSupplementalSubtitleLoggingInfo;
+    }
+
+    /** Returns the supplemental text. */
+    @Nullable
+    public Text getSupplementalText() {
+        return mSupplementalText;
+    }
+
+    /** Returns the supplemental icon. */
+    @Nullable
+    public Icon getSupplementalIcon() {
+        return mSupplementalIcon;
+    }
+
+    /** Returns the supplemental line's tap action. Can be null if not being set. */
+    @Nullable
+    public TapAction getSupplementalTapAction() {
+        return mSupplementalTapAction;
+    }
+
+    /** Returns the card's supplemental line logging info. */
+    @Nullable
+    public SubItemLoggingInfo getSupplementalLoggingInfo() {
+        return mSupplementalLoggingInfo;
+    }
+
     /** Returns the supplemental alarm text. */
     @Nullable
     public Text getSupplementalAlarmText() {
         return mSupplementalAlarmText;
     }
 
+    /** Returns the card layout weight info. Default weight is 0. */
+    public int getLayoutWeight() {
+        return mLayoutWeight;
+    }
+
     /**
      * @see Parcelable.Creator
      */
@@ -229,10 +330,17 @@
         out.writeTypedObject(mSubtitleText, flags);
         out.writeTypedObject(mSubtitleIcon, flags);
         out.writeTypedObject(mPrimaryTapAction, flags);
+        out.writeTypedObject(mPrimaryLoggingInfo, flags);
         out.writeTypedObject(mSupplementalSubtitleText, flags);
         out.writeTypedObject(mSupplementalSubtitleIcon, flags);
         out.writeTypedObject(mSupplementalSubtitleTapAction, flags);
+        out.writeTypedObject(mSupplementalSubtitleLoggingInfo, flags);
+        out.writeTypedObject(mSupplementalText, flags);
+        out.writeTypedObject(mSupplementalIcon, flags);
+        out.writeTypedObject(mSupplementalTapAction, flags);
+        out.writeTypedObject(mSupplementalLoggingInfo, flags);
         out.writeTypedObject(mSupplementalAlarmText, flags);
+        out.writeInt(mLayoutWeight);
     }
 
     @Override
@@ -246,19 +354,31 @@
                 && SmartspaceUtils.isEqual(mSubtitleText, that.mSubtitleText)
                 && Objects.equals(mSubtitleIcon, that.mSubtitleIcon)
                 && Objects.equals(mPrimaryTapAction, that.mPrimaryTapAction)
+                && Objects.equals(mPrimaryLoggingInfo, that.mPrimaryLoggingInfo)
                 && SmartspaceUtils.isEqual(mSupplementalSubtitleText,
                 that.mSupplementalSubtitleText)
                 && Objects.equals(mSupplementalSubtitleIcon, that.mSupplementalSubtitleIcon)
                 && Objects.equals(mSupplementalSubtitleTapAction,
                 that.mSupplementalSubtitleTapAction)
-                && SmartspaceUtils.isEqual(mSupplementalAlarmText, that.mSupplementalAlarmText);
+                && Objects.equals(mSupplementalSubtitleLoggingInfo,
+                that.mSupplementalSubtitleLoggingInfo)
+                && SmartspaceUtils.isEqual(mSupplementalText,
+                that.mSupplementalText)
+                && Objects.equals(mSupplementalIcon, that.mSupplementalIcon)
+                && Objects.equals(mSupplementalTapAction, that.mSupplementalTapAction)
+                && Objects.equals(mSupplementalLoggingInfo, that.mSupplementalLoggingInfo)
+                && SmartspaceUtils.isEqual(mSupplementalAlarmText, that.mSupplementalAlarmText)
+                && mLayoutWeight == that.mLayoutWeight;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mTemplateType, mTitleText, mTitleIcon, mSubtitleText, mSubtitleIcon,
-                mPrimaryTapAction, mSupplementalSubtitleText, mSupplementalSubtitleIcon,
-                mSupplementalSubtitleTapAction, mSupplementalAlarmText);
+                mPrimaryTapAction, mPrimaryLoggingInfo, mSupplementalSubtitleText,
+                mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction,
+                mSupplementalSubtitleLoggingInfo,
+                mSupplementalText, mSupplementalIcon, mSupplementalTapAction,
+                mSupplementalLoggingInfo, mSupplementalAlarmText, mLayoutWeight);
     }
 
     @Override
@@ -270,10 +390,17 @@
                 + ", mSubtitleText=" + mSubtitleText
                 + ", mSubTitleIcon=" + mSubtitleIcon
                 + ", mPrimaryTapAction=" + mPrimaryTapAction
+                + ", mPrimaryLoggingInfo=" + mPrimaryLoggingInfo
                 + ", mSupplementalSubtitleText=" + mSupplementalSubtitleText
                 + ", mSupplementalSubtitleIcon=" + mSupplementalSubtitleIcon
                 + ", mSupplementalSubtitleTapAction=" + mSupplementalSubtitleTapAction
+                + ", mSupplementalSubtitleLoggingInfo=" + mSupplementalSubtitleLoggingInfo
+                + ", mSupplementalText=" + mSupplementalText
+                + ", mSupplementalIcon=" + mSupplementalIcon
+                + ", mSupplementalTapAction=" + mSupplementalTapAction
+                + ", mSupplementalLoggingInfo=" + mSupplementalLoggingInfo
                 + ", mSupplementalAlarmText=" + mSupplementalAlarmText
+                + ", mLayoutWeight=" + mLayoutWeight
                 + '}';
     }
 
@@ -292,18 +419,26 @@
         private Text mSubtitleText;
         private Icon mSubtitleIcon;
         private TapAction mPrimaryTapAction;
+        private SubItemLoggingInfo mPrimaryLoggingInfo;
         private Text mSupplementalSubtitleText;
         private Icon mSupplementalSubtitleIcon;
         private TapAction mSupplementalSubtitleTapAction;
+        private SubItemLoggingInfo mSupplementalSubtitleLoggingInfo;
+        private Text mSupplementalText;
+        private Icon mSupplementalIcon;
+        private TapAction mSupplementalTapAction;
+        private SubItemLoggingInfo mSupplementalLoggingInfo;
         private Text mSupplementalAlarmText;
+        private int mLayoutWeight;
 
         /**
-         * A builder for {@link BaseTemplateData}.
+         * A builder for {@link BaseTemplateData}. By default sets the layout weight to be 0.
          *
          * @param templateType the {@link UiTemplateType} of this template data.
          */
         public Builder(@UiTemplateType int templateType) {
             mTemplateType = templateType;
+            mLayoutWeight = 0;
         }
 
         /** Should ONLY be used by the subclasses */
@@ -351,6 +486,13 @@
         /** Should ONLY be used by the subclasses */
         @Nullable
         @SuppressLint("GetterOnBuilder")
+        SubItemLoggingInfo getPrimaryLoggingInfo() {
+            return mPrimaryLoggingInfo;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
         Text getSupplementalSubtitleText() {
             return mSupplementalSubtitleText;
         }
@@ -372,10 +514,51 @@
         /** Should ONLY be used by the subclasses */
         @Nullable
         @SuppressLint("GetterOnBuilder")
+        SubItemLoggingInfo getSupplementalSubtitleLoggingInfo() {
+            return mSupplementalSubtitleLoggingInfo;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        Text getSupplementalText() {
+            return mSupplementalText;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        Icon getSupplementalIcon() {
+            return mSupplementalIcon;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        TapAction getSupplementalTapAction() {
+            return mSupplementalTapAction;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        SubItemLoggingInfo getSupplementalLoggingInfo() {
+            return mSupplementalLoggingInfo;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
         Text getSupplementalAlarmText() {
             return mSupplementalAlarmText;
         }
 
+        /** Should ONLY be used by the subclasses */
+        @SuppressLint("GetterOnBuilder")
+        int getLayoutWeight() {
+            return mLayoutWeight;
+        }
+
         /**
          * Sets the card title.
          */
@@ -422,6 +605,15 @@
         }
 
         /**
+         * Sets the card primary logging info.
+         */
+        @NonNull
+        public Builder setPrimaryLoggingInfo(@NonNull SubItemLoggingInfo primaryLoggingInfo) {
+            mPrimaryLoggingInfo = primaryLoggingInfo;
+            return this;
+        }
+
+        /**
          * Sets the supplemental subtitle text.
          */
         @NonNull
@@ -443,8 +635,7 @@
 
         /**
          * Sets the supplemental subtitle tap action. {@code mPrimaryTapAction} will be used if not
-         * being
-         * set.
+         * being set.
          */
         @NonNull
         public Builder setSupplementalSubtitleTapAction(
@@ -454,6 +645,54 @@
         }
 
         /**
+         * Sets the card supplemental title's logging info.
+         */
+        @NonNull
+        public Builder setSupplementalSubtitleLoggingInfo(
+                @NonNull SubItemLoggingInfo supplementalSubtitleLoggingInfo) {
+            mSupplementalSubtitleLoggingInfo = supplementalSubtitleLoggingInfo;
+            return this;
+        }
+
+        /**
+         * Sets the supplemental text.
+         */
+        @NonNull
+        public Builder setSupplementalText(@NonNull Text supplementalText) {
+            mSupplementalText = supplementalText;
+            return this;
+        }
+
+        /**
+         * Sets the supplemental icon.
+         */
+        @NonNull
+        public Builder setSupplementalIcon(@NonNull Icon supplementalIcon) {
+            mSupplementalIcon = supplementalIcon;
+            return this;
+        }
+
+        /**
+         * Sets the supplemental line tap action. {@code mPrimaryTapAction} will be used if not
+         * being set.
+         */
+        @NonNull
+        public Builder setSupplementalTapAction(@NonNull TapAction supplementalTapAction) {
+            mSupplementalTapAction = supplementalTapAction;
+            return this;
+        }
+
+        /**
+         * Sets the card supplemental line's logging info.
+         */
+        @NonNull
+        public Builder setSupplementalLoggingInfo(
+                @NonNull SubItemLoggingInfo supplementalLoggingInfo) {
+            mSupplementalLoggingInfo = supplementalLoggingInfo;
+            return this;
+        }
+
+        /**
          * Sets the supplemental alarm text.
          */
         @NonNull
@@ -463,14 +702,136 @@
         }
 
         /**
+         * Sets the layout weight.
+         */
+        @NonNull
+        public Builder setLayoutWeight(int layoutWeight) {
+            mLayoutWeight = layoutWeight;
+            return this;
+        }
+
+        /**
          * Builds a new SmartspaceDefaultUiTemplateData instance.
          */
         @NonNull
         public BaseTemplateData build() {
             return new BaseTemplateData(mTemplateType, mTitleText, mTitleIcon,
-                    mSubtitleText, mSubtitleIcon, mPrimaryTapAction, mSupplementalSubtitleText,
-                    mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction,
-                    mSupplementalAlarmText);
+                    mSubtitleText, mSubtitleIcon, mPrimaryTapAction,
+                    mPrimaryLoggingInfo,
+                    mSupplementalSubtitleText, mSupplementalSubtitleIcon,
+                    mSupplementalSubtitleTapAction, mSupplementalSubtitleLoggingInfo,
+                    mSupplementalText, mSupplementalIcon,
+                    mSupplementalTapAction, mSupplementalLoggingInfo,
+                    mSupplementalAlarmText, mLayoutWeight);
+        }
+    }
+
+    /**
+     * Holds all the logging info needed for a sub item within the base card. For example, the
+     * supplemental-subtitle part should have its own logging info.
+     */
+    public static final class SubItemLoggingInfo implements Parcelable {
+
+        /** A unique instance id for the sub item. */
+        private final int mInstanceId;
+
+        /** The feature type for this sub item. */
+        private final int mFeatureType;
+
+        SubItemLoggingInfo(@NonNull Parcel in) {
+            mInstanceId = in.readInt();
+            mFeatureType = in.readInt();
+        }
+
+        private SubItemLoggingInfo(int instanceId, int featureType) {
+            mInstanceId = instanceId;
+            mFeatureType = featureType;
+        }
+
+        public int getInstanceId() {
+            return mInstanceId;
+        }
+
+        public int getFeatureType() {
+            return mFeatureType;
+        }
+
+        /**
+         * @see Parcelable.Creator
+         */
+        @NonNull
+        public static final Creator<SubItemLoggingInfo> CREATOR =
+                new Creator<SubItemLoggingInfo>() {
+                    @Override
+                    public SubItemLoggingInfo createFromParcel(Parcel in) {
+                        return new SubItemLoggingInfo(in);
+                    }
+
+                    @Override
+                    public SubItemLoggingInfo[] newArray(int size) {
+                        return new SubItemLoggingInfo[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeInt(mInstanceId);
+            out.writeInt(mFeatureType);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof SubItemLoggingInfo)) return false;
+            SubItemLoggingInfo that = (SubItemLoggingInfo) o;
+            return mInstanceId == that.mInstanceId && mFeatureType == that.mFeatureType;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mInstanceId, mFeatureType);
+        }
+
+        @Override
+        public String toString() {
+            return "SubItemLoggingInfo{"
+                    + "mInstanceId=" + mInstanceId
+                    + ", mFeatureType=" + mFeatureType
+                    + '}';
+        }
+
+        /**
+         * A builder for {@link SubItemLoggingInfo} object.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final class Builder {
+
+            private final int mInstanceId;
+            private final int mFeatureType;
+
+            /**
+             * A builder for {@link SubItemLoggingInfo}.
+             *
+             * @param instanceId  A unique instance id for the sub item
+             * @param featureType The feature type for this sub item
+             */
+            public Builder(int instanceId, int featureType) {
+                mInstanceId = instanceId;
+                mFeatureType = featureType;
+            }
+
+            /** Builds a new {@link SubItemLoggingInfo} instance. */
+            @NonNull
+            public SubItemLoggingInfo build() {
+                return new SubItemLoggingInfo(mInstanceId, mFeatureType);
+            }
         }
     }
 }
diff --git a/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java
index feb1c34..fbdb7be 100644
--- a/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java
@@ -59,17 +59,29 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @NonNull List<CarouselItem> carouselItems,
             @Nullable TapAction carouselAction) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
+
         mCarouselItems = carouselItems;
         mCarouselAction = carouselAction;
     }
@@ -179,10 +191,14 @@
             }
 
             return new CarouselTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mCarouselItems,
-                    mCarouselAction);
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
+                    mCarouselItems, mCarouselAction);
         }
     }
 
diff --git a/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java
index 13091e2..1d13066 100644
--- a/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java
@@ -54,16 +54,27 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @NonNull List<BaseTemplateData> combinedCardDataList) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
         mCombinedCardDataList = combinedCardDataList;
     }
 
@@ -151,9 +162,13 @@
                 throw new IllegalStateException("Please assign a value to all @NonNull args.");
             }
             return new CombinedCardsTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
                     mCombinedCardDataList);
         }
     }
diff --git a/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java
index eb56e93..19177df 100644
--- a/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java
@@ -69,21 +69,33 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @Nullable Text headToHeadTitle,
             @Nullable Icon headToHeadFirstCompetitorIcon,
             @Nullable Icon headToHeadSecondCompetitorIcon,
             @Nullable Text headToHeadFirstCompetitorText,
             @Nullable Text headToHeadSecondCompetitorText,
             @Nullable TapAction headToHeadAction) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
+
         mHeadToHeadTitle = headToHeadTitle;
         mHeadToHeadFirstCompetitorIcon = headToHeadFirstCompetitorIcon;
         mHeadToHeadSecondCompetitorIcon = headToHeadSecondCompetitorIcon;
@@ -285,9 +297,13 @@
         @NonNull
         public HeadToHeadTemplateData build() {
             return new HeadToHeadTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
                     mHeadToHeadTitle,
                     mHeadToHeadFirstCompetitorIcon,
                     mHeadToHeadSecondCompetitorIcon, mHeadToHeadFirstCompetitorText,
diff --git a/core/java/android/app/smartspace/uitemplatedata/Icon.java b/core/java/android/app/smartspace/uitemplatedata/Icon.java
index 2b1f420..6bdc926 100644
--- a/core/java/android/app/smartspace/uitemplatedata/Icon.java
+++ b/core/java/android/app/smartspace/uitemplatedata/Icon.java
@@ -69,7 +69,10 @@
         return mContentDescription;
     }
 
-    /** Return shouldTint value. The default value is true. */
+    /**
+     * Return shouldTint value, which means whether should tint the icon with the system's theme
+     * color. The default value is true.
+     */
     public boolean shouldTint() {
         return mShouldTint;
     }
@@ -155,7 +158,7 @@
         }
 
         /**
-         * Sets should tint icon.
+         * Sets should tint icon with the system's theme color.
          */
         @NonNull
         public Builder setShouldTint(boolean shouldTint) {
diff --git a/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java
index 9c8330d..48af9c1 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java
@@ -62,18 +62,30 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @NonNull Icon subCardIcon,
             @Nullable Text subCardText,
             @Nullable TapAction subCardAction) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
+
         mSubCardIcon = subCardIcon;
         mSubCardText = subCardText;
         mSubCardAction = subCardAction;
@@ -196,9 +208,14 @@
         @NonNull
         public SubCardTemplateData build() {
             return new SubCardTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubCardIcon,
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
+                    mSubCardIcon,
                     mSubCardText,
                     mSubCardAction);
         }
diff --git a/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java
index 7df5238..38692cd 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java
@@ -63,18 +63,30 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @NonNull List<Text> subImageTexts,
             @NonNull List<Icon> subImages,
             @Nullable TapAction subImageAction) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
+
         mSubImageTexts = subImageTexts;
         mSubImages = subImages;
         mSubImageAction = subImageAction;
@@ -193,9 +205,14 @@
         @NonNull
         public SubImageTemplateData build() {
             return new SubImageTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubImageTexts,
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
+                    mSubImageTexts,
                     mSubImages,
                     mSubImageAction);
         }
diff --git a/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java
index 6f6034d..b1535f1 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java
@@ -62,18 +62,30 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @Nullable Icon subListIcon,
             @NonNull List<Text> subListTexts,
             @Nullable TapAction subListAction) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
+
         mSubListIcon = subListIcon;
         mSubListTexts = subListTexts;
         mSubListAction = subListAction;
@@ -196,9 +208,14 @@
         @NonNull
         public SubListTemplateData build() {
             return new SubListTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubListIcon,
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
+                    mSubListIcon,
                     mSubListTexts,
                     mSubListAction);
         }
diff --git a/core/java/android/app/smartspace/uitemplatedata/TapAction.java b/core/java/android/app/smartspace/uitemplatedata/TapAction.java
index 83ff6ab..b8e1afb 100644
--- a/core/java/android/app/smartspace/uitemplatedata/TapAction.java
+++ b/core/java/android/app/smartspace/uitemplatedata/TapAction.java
@@ -58,7 +58,13 @@
     private final UserHandle mUserHandle;
 
     @Nullable
-    private Bundle mExtras;
+    private final Bundle mExtras;
+
+    /**
+     * Whether the tap action's result should be shown on the lockscreen (e.g. turn off the
+     * flashlight can be done on LS bypassing the keyguard). Default value is false.
+     */
+    private final boolean mShouldShowOnLockscreen;
 
     TapAction(@NonNull Parcel in) {
         mId = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
@@ -66,16 +72,18 @@
         mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
         mUserHandle = in.readTypedObject(UserHandle.CREATOR);
         mExtras = in.readBundle();
+        mShouldShowOnLockscreen = in.readBoolean();
     }
 
     private TapAction(@Nullable CharSequence id, @Nullable Intent intent,
             @Nullable PendingIntent pendingIntent, @Nullable UserHandle userHandle,
-            @Nullable Bundle extras) {
+            @Nullable Bundle extras, boolean shouldShowOnLockscreen) {
         mId = id;
         mIntent = intent;
         mPendingIntent = pendingIntent;
         mUserHandle = userHandle;
         mExtras = extras;
+        mShouldShowOnLockscreen = shouldShowOnLockscreen;
     }
 
     /** Returns the unique id of the tap action. */
@@ -110,6 +118,14 @@
         return mExtras;
     }
 
+    /**
+     * Whether the tap action's result should be shown on the lockscreen. If true, the tap action's
+     * handling should bypass the keyguard. Default value is false.
+     */
+    public boolean shouldShowOnLockscreen() {
+        return mShouldShowOnLockscreen;
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
         TextUtils.writeToParcel(mId, out, flags);
@@ -117,6 +133,7 @@
         out.writeTypedObject(mPendingIntent, flags);
         out.writeTypedObject(mUserHandle, flags);
         out.writeBundle(mExtras);
+        out.writeBoolean(mShouldShowOnLockscreen);
     }
 
     @Override
@@ -158,6 +175,7 @@
                 + ", mPendingIntent=" + mPendingIntent
                 + ", mUserHandle=" + mUserHandle
                 + ", mExtras=" + mExtras
+                + ", mShouldShowOnLockscreen=" + mShouldShowOnLockscreen
                 + '}';
     }
 
@@ -174,14 +192,16 @@
         private PendingIntent mPendingIntent;
         private UserHandle mUserHandle;
         private Bundle mExtras;
+        private boolean mShouldShowOnLockScreen;
 
         /**
-         * A builder for {@link TapAction}.
+         * A builder for {@link TapAction}. By default sets should_show_on_lockscreen to false.
          *
          * @param id A unique Id of this {@link TapAction}.
          */
         public Builder(@NonNull CharSequence id) {
             mId = Objects.requireNonNull(id);
+            mShouldShowOnLockScreen = false;
         }
 
         /**
@@ -222,6 +242,16 @@
         }
 
         /**
+         * Sets whether the tap action's result should be shown on the lockscreen, to bypass the
+         * keyguard when the tap action is triggered.
+         */
+        @NonNull
+        public Builder setShouldShowOnLockscreen(@NonNull boolean shouldShowOnLockScreen) {
+            mShouldShowOnLockScreen = shouldShowOnLockScreen;
+            return this;
+        }
+
+        /**
          * Builds a new SmartspaceTapAction instance.
          *
          * @throws IllegalStateException if the tap action is empty.
@@ -231,7 +261,8 @@
             if (mIntent == null && mPendingIntent == null && mExtras == null) {
                 throw new IllegalStateException("Please assign at least 1 valid tap field");
             }
-            return new TapAction(mId, mIntent, mPendingIntent, mUserHandle, mExtras);
+            return new TapAction(mId, mIntent, mPendingIntent, mUserHandle, mExtras,
+                    mShouldShowOnLockScreen);
         }
     }
 }
diff --git a/core/java/android/app/smartspace/uitemplatedata/Text.java b/core/java/android/app/smartspace/uitemplatedata/Text.java
index b733394..e1afce7 100644
--- a/core/java/android/app/smartspace/uitemplatedata/Text.java
+++ b/core/java/android/app/smartspace/uitemplatedata/Text.java
@@ -131,16 +131,6 @@
         }
 
         /**
-         * A builder for {@link Text} with specifying {@link TextUtils.TruncateAt} type, and by
-         * default set the max lines to 1.
-         */
-        public Builder(@NonNull CharSequence text, @NonNull TextUtils.TruncateAt truncateAtType) {
-            mText = Objects.requireNonNull(text);
-            mTruncateAtType = Objects.requireNonNull(truncateAtType);
-            mMaxLines = 1;
-        }
-
-        /**
          * Sets truncateAtType, where the text content should be truncated if not all the content
          * can be presented.
          */
diff --git a/core/java/android/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java
index 4e00da1..47bec61 100644
--- a/core/java/android/attention/AttentionManagerInternal.java
+++ b/core/java/android/attention/AttentionManagerInternal.java
@@ -49,21 +49,19 @@
     /**
      * Requests the continuous updates of proximity signal via the provided callback,
      * until the given callback is unregistered. Currently, AttentionManagerService only
-     * anticipates one client and updates one client at a time. If a new client wants to
-     * onboard to receiving Proximity updates, please make a feature request to make proximity
-     * feature multi-client before depending on this feature.
+     * anticipates one client and updates one client at a time.
      *
      * @param callback      a callback that receives the proximity updates
      * @return {@code true} if the registration should succeed.
      */
-    public abstract boolean onStartProximityUpdates(ProximityCallbackInternal callback);
+    public abstract boolean onStartProximityUpdates(ProximityUpdateCallbackInternal callback);
 
     /**
      * Requests to stop providing continuous updates until the callback is registered.
      *
      * @param callback a callback that was used in {@link #onStartProximityUpdates}
      */
-    public abstract void onStopProximityUpdates(ProximityCallbackInternal callback);
+    public abstract void onStopProximityUpdates(ProximityUpdateCallbackInternal callback);
 
     /** Internal interface for attention callback. */
     public abstract static class AttentionCallbackInternal {
@@ -85,7 +83,7 @@
     }
 
     /** Internal interface for proximity callback. */
-    public abstract static class ProximityCallbackInternal {
+    public abstract static class ProximityUpdateCallbackInternal {
         /**
          * @param distance the estimated distance of the user (in meter)
          * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index d50a6ba..99ce147 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -148,6 +148,8 @@
                         }
                     }
                 };
+        @Nullable
+        private VirtualAudioDevice mVirtualAudioDevice;
 
         private VirtualDevice(
                 IVirtualDeviceManager service,
@@ -255,8 +257,8 @@
         }
 
         /**
-         * Closes the virtual device, stopping and tearing down any virtual displays,
-         * audio policies, and event injection that's currently in progress.
+         * Closes the virtual device, stopping and tearing down any virtual displays, associated
+         * virtual audio device, and event injection that's currently in progress.
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void close() {
@@ -265,6 +267,10 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
+            if (mVirtualAudioDevice != null) {
+                mVirtualAudioDevice.close();
+                mVirtualAudioDevice = null;
+            }
         }
 
         /**
@@ -351,8 +357,10 @@
          * Creates a VirtualAudioDevice, capable of recording audio emanating from this device,
          * or injecting audio from another device.
          *
-         * <p>Note: This object does not support capturing privileged playback, such as voice call
-         * audio.
+         * <p>Note: One {@link VirtualDevice} can only create one {@link VirtualAudioDevice}, so
+         * calling this method multiple times will return the same instance. When
+         * {@link VirtualDevice#close()} is called, the associated {@link VirtualAudioDevice} will
+         * also be closed automatically.
          *
          * @param display The target virtual display to capture from and inject into.
          * @param executor The {@link Executor} object for the thread on which to execute
@@ -368,7 +376,11 @@
                 @NonNull VirtualDisplay display,
                 @Nullable Executor executor,
                 @Nullable AudioConfigurationChangeCallback callback) {
-            return new VirtualAudioDevice(mContext, mVirtualDevice, display, executor, callback);
+            if (mVirtualAudioDevice == null) {
+                mVirtualAudioDevice = new VirtualAudioDevice(
+                        mContext, mVirtualDevice, display, executor, callback);
+            }
+            return mVirtualAudioDevice;
         }
 
         /**
diff --git a/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java b/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java
index 5c246d3..c816da7 100644
--- a/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java
+++ b/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java
@@ -60,7 +60,6 @@
     /** Registers user restrictions change. */
     void register(@NonNull UserRestrictionsCallback callback) {
         mUserRestrictionsCallback = callback;
-
         IntentFilter filter = new IntentFilter();
         filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
         mContext.registerReceiver(/* receiver= */ this, filter);
@@ -73,8 +72,10 @@
 
     /** Unregisters user restrictions change. */
     void unregister() {
-        mUserRestrictionsCallback = null;
-        mContext.unregisterReceiver(/* receiver= */ this);
+        if (mUserRestrictionsCallback != null) {
+            mUserRestrictionsCallback = null;
+            mContext.unregisterReceiver(/* receiver= */ this);
+        }
     }
 
     @Override
diff --git a/core/java/android/companion/virtual/audio/VirtualAudioDevice.java b/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
index 38e37ec..3f7299f 100644
--- a/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
+++ b/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.hardware.display.VirtualDisplay;
 import android.media.AudioFormat;
+import android.media.AudioManager;
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioRecordingConfiguration;
 import android.os.RemoteException;
@@ -96,7 +97,7 @@
 
         if (mOngoingSession != null && mOngoingSession.getAudioInjection() != null) {
             throw new IllegalStateException("Cannot start an audio injection while a session is "
-                    + "ongoing. Call close() on this device first to end the previous injection.");
+                    + "ongoing. Call close() on this device first to end the previous session.");
         }
         if (mOngoingSession == null) {
             mOngoingSession = new VirtualAudioSession(mContext, mCallback, mExecutor);
@@ -114,6 +115,9 @@
     /**
      * Begins recording audio emanating from this device.
      *
+     * <p>Note: This method does not support capturing privileged playback, which means the
+     * application can opt out of capturing by {@link AudioManager#setAllowedCapturePolicy(int)}.
+     *
      * @return An {@link AudioCapture} containing the recorded audio.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
@@ -150,6 +154,7 @@
         return mOngoingSession != null ? mOngoingSession.getAudioInjection() : null;
     }
 
+    /** Stops audio capture and injection then releases all the resources */
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     @Override
     public void close() {
diff --git a/core/java/android/companion/virtual/audio/VirtualAudioSession.java b/core/java/android/companion/virtual/audio/VirtualAudioSession.java
index bc71bd6..c6a1045 100644
--- a/core/java/android/companion/virtual/audio/VirtualAudioSession.java
+++ b/core/java/android/companion/virtual/audio/VirtualAudioSession.java
@@ -120,7 +120,7 @@
     @NonNull
     public AudioInjection startAudioInjection(@NonNull AudioFormat injectionFormat) {
         Objects.requireNonNull(injectionFormat, "injectionFormat must not be null");
-        mUserRestrictionsDetector.register(/* callback= */ this);
+
         synchronized (mLock) {
             if (mAudioInjection != null) {
                 throw new IllegalStateException(
@@ -130,6 +130,8 @@
             mInjectionFormat = injectionFormat;
             mAudioInjection = new AudioInjection();
             mAudioInjection.play();
+
+            mUserRestrictionsDetector.register(/* callback= */ this);
             mAudioInjection.setSilent(mUserRestrictionsDetector.isUnmuteMicrophoneDisallowed());
             return mAudioInjection;
         }
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 45d4c09..6113932 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -17,6 +17,7 @@
 package android.hardware;
 
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.compat.annotation.UnsupportedAppUsage;
 
 /**
@@ -664,16 +665,20 @@
      * The first three elements provide the transform from the (arbitrary, possibly slowly drifting)
      * reference frame to the head frame. The magnitude of this vector is in range [0, &pi;]
      * radians, while the value of individual axes is in range [-&pi;, &pi;]. The next three
-     * elements provide the estimated rotational velocity of the user's head relative to itself, in
-     * radians per second.
+     * elements optionally provide the estimated rotational velocity of the user's head relative to
+     * itself, in radians per second. If a given sensor does not support determining velocity, these
+     * elements are set to 0.
      *
      * <ul>
      *  <li> values[0] : X component of Euler vector representing rotation</li>
      *  <li> values[1] : Y component of Euler vector representing rotation</li>
      *  <li> values[2] : Z component of Euler vector representing rotation</li>
-     *  <li> values[3] : X component of Euler vector representing angular velocity</li>
-     *  <li> values[4] : Y component of Euler vector representing angular velocity</li>
-     *  <li> values[5] : Z component of Euler vector representing angular velocity</li>
+     *  <li> values[3] : X component of Euler vector representing angular velocity (if
+     *  supported, otherwise 0)</li>
+     *  <li> values[4] : Y component of Euler vector representing angular velocity (if
+     *  supported, otherwise 0)</li>
+     *  <li> values[5] : Z component of Euler vector representing angular velocity (if
+     *  supported, otherwise 0)</li>
      * </ul>
      *
      * <h4>{@link android.hardware.Sensor#TYPE_ACCELEROMETER_LIMITED_AXES
@@ -820,6 +825,21 @@
      */
     public long timestamp;
 
+    /**
+     * Set to true when this is the first sensor event after a discontinuity.
+     *
+     * The exact meaning of discontinuity depends on the sensor type. For
+     * {@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER}, this means that
+     * the reference frame has suddenly and significantly changed, for example if the head tracking
+     * device was removed then put back.
+     *
+     * Note that this concept is either not relevant to or not supported by most sensor types,
+     * {@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER} being the notable
+     * exception.
+     */
+    @SuppressLint("MutableBareField")
+    public boolean firstEventAfterDiscontinuity;
+
     @UnsupportedAppUsage
     SensorEvent(int valueSize) {
         values = new float[valueSize];
diff --git a/core/java/android/hardware/SensorEventCallback.java b/core/java/android/hardware/SensorEventCallback.java
index 7b0092d..bac212a 100644
--- a/core/java/android/hardware/SensorEventCallback.java
+++ b/core/java/android/hardware/SensorEventCallback.java
@@ -16,8 +16,6 @@
 
 package android.hardware;
 
-import android.annotation.NonNull;
-
 /**
  * Used for receiving sensor additional information frames.
  */
@@ -54,21 +52,4 @@
      * reported from sensor hardware.
      */
     public void onSensorAdditionalInfo(SensorAdditionalInfo info) {}
-
-    /**
-     * Called when the next {@link android.hardware.SensorEvent SensorEvent} to be delivered via the
-     * {@link #onSensorChanged(SensorEvent) onSensorChanged} method represents the first event after
-     * a discontinuity.
-     *
-     * The exact meaning of discontinuity depends on the sensor type. For {@link
-     * android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER}, this means that the
-     * reference frame has suddenly and significantly changed.
-     *
-     * Note that this concept is either not relevant to or not supported by most sensor types,
-     * {@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER} being the notable
-     * exception.
-     *
-     * @param sensor The {@link android.hardware.Sensor Sensor} which experienced the discontinuity.
-     */
-    public void onSensorDiscontinuity(@NonNull Sensor sensor) {}
 }
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 4399af9..a3cc01c 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -158,36 +158,30 @@
 
     }
 
+
+    /**
+     * Constant for software toggle.
+     */
+    public static final int TOGGLE_TYPE_SOFTWARE =
+            SensorPrivacyIndividualEnabledSensorProto.SOFTWARE;
+
+    /**
+     * Constant for hardware toggle.
+     */
+    public static final int TOGGLE_TYPE_HARDWARE =
+            SensorPrivacyIndividualEnabledSensorProto.HARDWARE;
+
     /**
      * Types of toggles which can exist for sensor privacy
+     *
      * @hide
      */
-    public static class ToggleTypes {
-        private ToggleTypes() {}
-
-        /**
-         * Constant for software toggle.
-         */
-        public static final int SOFTWARE = SensorPrivacyIndividualEnabledSensorProto.SOFTWARE;
-
-        /**
-         * Constant for hardware toggle.
-         */
-        public static final int HARDWARE = SensorPrivacyIndividualEnabledSensorProto.HARDWARE;
-
-        /**
-         * Types of toggles which can exist for sensor privacy
-         *
-         * @hide
-         */
-        @IntDef(value = {
-                SOFTWARE,
-                HARDWARE
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface ToggleType {}
-
-    }
+    @IntDef(value = {
+            TOGGLE_TYPE_SOFTWARE,
+            TOGGLE_TYPE_HARDWARE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ToggleType {}
 
     /**
      * Types of state which can exist for the sensor privacy toggle
@@ -232,20 +226,23 @@
         /**
          * Callback invoked when the sensor privacy state changes.
          *
+         * @param params Parameters describing the new state
+         */
+        default void onSensorPrivacyChanged(@NonNull SensorPrivacyChangedParams params) {
+            onSensorPrivacyChanged(params.mSensor, params.mEnabled);
+        }
+
+        /**
+         * Callback invoked when the sensor privacy state changes.
+         *
          * @param sensor the sensor whose state is changing
          * @param enabled true if sensor privacy is enabled, false otherwise.
+         *
+         * @deprecated Please use
+         * {@link #onSensorPrivacyChanged(SensorPrivacyChangedParams)}
          */
+        @Deprecated
         void onSensorPrivacyChanged(int sensor, boolean enabled);
-    }
-
-    /**
-     * A class implementing this interface can register with the {@link
-     * android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy
-     * state changes.
-     *
-     * @hide
-     */
-    public interface OnToggleSensorPrivacyChangedListener {
 
         /**
          * A class containing information about what the sensor privacy state has changed to.
@@ -262,7 +259,7 @@
                 mEnabled = enabled;
             }
 
-            public @ToggleTypes.ToggleType int getToggleType() {
+            public @ToggleType int getToggleType() {
                 return mToggleType;
             }
 
@@ -274,13 +271,6 @@
                 return mEnabled;
             }
         }
-
-        /**
-         * Callback invoked when the sensor privacy state changes.
-         *
-         * @param params Parameters describing the new state
-         */
-        void onSensorPrivacyChanged(@NonNull SensorPrivacyChangedParams params);
     }
 
     private static final Object sInstanceLock = new Object();
@@ -305,15 +295,15 @@
     /** Registered listeners */
     @GuardedBy("mLock")
     @NonNull
-    private final ArrayMap<OnToggleSensorPrivacyChangedListener, Executor> mToggleListeners =
+    private final ArrayMap<OnSensorPrivacyChangedListener, Executor> mToggleListeners =
             new ArrayMap<>();
 
     /** Listeners registered using the deprecated APIs and which
-     * OnToggleSensorPrivacyChangedListener they're using. */
+     * OnSensorPrivacyChangedListener they're using. */
     @GuardedBy("mLock")
     @NonNull
     private final ArrayMap<Pair<Integer, OnSensorPrivacyChangedListener>,
-            OnToggleSensorPrivacyChangedListener> mLegacyToggleListeners = new ArrayMap<>();
+            OnSensorPrivacyChangedListener> mLegacyToggleListeners = new ArrayMap<>();
 
     /** The singleton ISensorPrivacyListener for IPC which will be used to dispatch to local
      * listeners */
@@ -323,9 +313,9 @@
         public void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled) {
             synchronized (mLock) {
                 for (int i = 0; i < mToggleListeners.size(); i++) {
-                    OnToggleSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i);
+                    OnSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i);
                     mToggleListeners.valueAt(i).execute(() -> listener
-                            .onSensorPrivacyChanged(new OnToggleSensorPrivacyChangedListener
+                            .onSensorPrivacyChanged(new OnSensorPrivacyChangedListener
                                     .SensorPrivacyChangedParams(toggleType, sensor, enabled)));
                 }
             }
@@ -367,12 +357,24 @@
     }
 
     /**
+     * Returns the single instance of the SensorPrivacyManager.
+     *
+     * @hide
+     */
+    public static SensorPrivacyManager getInstance(Context context, ISensorPrivacyManager service) {
+        synchronized (sInstanceLock) {
+            sInstance = new SensorPrivacyManager(context, service);
+            return sInstance;
+        }
+    }
+
+    /**
      * Checks if the given toggle is supported on this device
      * @param sensor The sensor to check
      * @return whether the toggle for the sensor is supported on this device.
      */
     public boolean supportsSensorToggle(@Sensors.Sensor int sensor) {
-        return supportsSensorToggle(ToggleTypes.SOFTWARE, sensor);
+        return supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor);
     }
 
     /**
@@ -380,10 +382,8 @@
      * @param sensor The sensor to check
      * @return whether the toggle for the sensor is supported on this device.
      *
-     * @hide
      */
-    public boolean supportsSensorToggle(@ToggleTypes.ToggleType int toggleType,
-            @Sensors.Sensor int sensor) {
+    public boolean supportsSensorToggle(@ToggleType int toggleType, @Sensors.Sensor int sensor) {
         try {
             Pair key = new Pair(toggleType, sensor);
             synchronized (mLock) {
@@ -408,8 +408,6 @@
      * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
      *                       privacy changes.
      *
-     * {@link #addSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)}
-     *
      * @hide
      */
     @SystemApi
@@ -429,8 +427,6 @@
      * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
      *                 privacy changes.
      *
-     * {@link #addSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)}
-     *
      * @hide
      */
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
@@ -449,19 +445,22 @@
      * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
      *                       privacy changes.
      *
-     * {@link #addSensorPrivacyListener(Executor, OnToggleSensorPrivacyChangedListener)}
-     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
             @NonNull OnSensorPrivacyChangedListener listener) {
-
         Pair<Integer, OnSensorPrivacyChangedListener> pair = new Pair(sensor, listener);
-        OnToggleSensorPrivacyChangedListener toggleListener = params -> {
-            if (params.getSensor() == sensor) {
-                listener.onSensorPrivacyChanged(params.getSensor(), params.isEnabled());
+        OnSensorPrivacyChangedListener toggleListener = new OnSensorPrivacyChangedListener() {
+            @Override
+            public void onSensorPrivacyChanged(SensorPrivacyChangedParams params) {
+                if (params.getSensor() == sensor) {
+                    listener.onSensorPrivacyChanged(params.getSensor(), params.isEnabled());
+                }
+            }
+            @Override
+            public void onSensorPrivacyChanged(int sensor, boolean enabled) {
             }
         };
 
@@ -476,13 +475,14 @@
      * Registers a new listener to receive notification when the state of sensor privacy
      * changes.
      *
-     * @param listener the OnToggleSensorPrivacyChangedListener to be notified when the state of
+     * @param listener the OnSensorPrivacyChangedListener to be notified when the state of
      *                 sensor privacy changes.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
-    public void addSensorPrivacyListener(@NonNull OnToggleSensorPrivacyChangedListener listener) {
+    public void addSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) {
         addSensorPrivacyListener(mContext.getMainExecutor(), listener);
     }
 
@@ -492,14 +492,15 @@
      * changes.
      *
      * @param executor the executor to dispatch the callback on
-     * @param listener the OnToggleSensorPrivacyChangedListener to be notified when the state of
+     * @param listener the OnSensorPrivacyChangedListener to be notified when the state of
      *                 sensor privacy changes.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public void addSensorPrivacyListener(@NonNull Executor executor,
-            @NonNull OnToggleSensorPrivacyChangedListener listener) {
+            @NonNull OnSensorPrivacyChangedListener listener) {
         synchronized (mLock) {
             addSensorPrivacyListenerLocked(executor, listener);
         }
@@ -507,7 +508,7 @@
 
     @GuardedBy("mLock")
     private void addSensorPrivacyListenerLocked(@NonNull Executor executor,
-            @NonNull OnToggleSensorPrivacyChangedListener listener) {
+            @NonNull OnSensorPrivacyChangedListener listener) {
         if (!mToggleListenerRegistered) {
             try {
                 mService.addToggleSensorPrivacyListener(mIToggleListener);
@@ -529,10 +530,6 @@
      * @param listener the OnSensorPrivacyChangedListener to be unregistered from notifications when
      *                 sensor privacy changes.
      *
-     * {@link #removeSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)} with
-     * {@link #addSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)} or
-     * {@link #addSensorPrivacyListener(Executor, OnToggleSensorPrivacyChangedListener)}
-     *
      * @hide
      */
     @SystemApi
@@ -541,7 +538,7 @@
             @NonNull OnSensorPrivacyChangedListener listener) {
         Pair<Integer, OnSensorPrivacyChangedListener> pair = new Pair(sensor, listener);
         synchronized (mLock) {
-            OnToggleSensorPrivacyChangedListener onToggleSensorPrivacyChangedListener =
+            OnSensorPrivacyChangedListener onToggleSensorPrivacyChangedListener =
                     mLegacyToggleListeners.remove(pair);
             if (onToggleSensorPrivacyChangedListener != null) {
                 removeSensorPrivacyListenerLocked(onToggleSensorPrivacyChangedListener);
@@ -553,14 +550,15 @@
      * Unregisters the specified listener from receiving notifications when the state of any sensor
      * privacy changes.
      *
-     * @param listener the {@link OnToggleSensorPrivacyChangedListener} to be unregistered from
+     * @param listener the {@link OnSensorPrivacyChangedListener} to be unregistered from
      *                 notifications when sensor privacy changes.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public void removeSensorPrivacyListener(
-            @NonNull OnToggleSensorPrivacyChangedListener listener) {
+            @NonNull OnSensorPrivacyChangedListener listener) {
         synchronized (mLock) {
             removeSensorPrivacyListenerLocked(listener);
         }
@@ -568,7 +566,7 @@
 
     @GuardedBy("mLock")
     private void removeSensorPrivacyListenerLocked(
-            @NonNull OnToggleSensorPrivacyChangedListener listener) {
+            @NonNull OnSensorPrivacyChangedListener listener) {
         mToggleListeners.remove(listener);
         if (mToggleListeners.size() == 0) {
             try {
@@ -586,12 +584,15 @@
      *
      * @return true if sensor privacy is currently enabled, false otherwise.
      *
+     * @deprecated Prefer to use {@link #isSensorPrivacyEnabled(int, int)}
+     *
      * @hide
      */
+    @Deprecated
     @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
-        return isSensorPrivacyEnabled(ToggleTypes.SOFTWARE, sensor);
+        return isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor);
     }
 
     /**
@@ -601,8 +602,9 @@
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
-    public boolean isSensorPrivacyEnabled(@ToggleTypes.ToggleType int toggleType,
+    public boolean isSensorPrivacyEnabled(@ToggleType int toggleType,
             @Sensors.Sensor int sensor) {
         try {
             return mService.isToggleSensorPrivacyEnabled(toggleType, sensor);
@@ -613,14 +615,16 @@
 
     /**
      * Returns whether sensor privacy is currently enabled for a specific sensor.
-     * Combines the state of the SW + HW toggles and returns the actual privacy state.
+     * Combines the state of the SW + HW toggles and returns true if either the
+     * SOFTWARE or the HARDWARE toggles are enabled.
      *
      * @return true if sensor privacy is currently enabled, false otherwise.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
-    public boolean isToggleSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
+    public boolean areAnySensorPrivacyTogglesEnabled(@Sensors.Sensor int sensor) {
         try {
             return mService.isCombinedToggleSensorPrivacyEnabled(sensor);
         } catch (RemoteException e) {
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 32a5ee7..18d86d6 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -881,13 +881,13 @@
                 mListener.onAccuracyChanged(t.sensor, t.accuracy);
             }
 
-            // call onSensorDiscontinuity() if the discontinuity counter changed
-            if (t.sensor.getType() == Sensor.TYPE_HEAD_TRACKER
-                    && mListener instanceof SensorEventCallback) {
+            // Indicate if the discontinuity count changed
+            if (t.sensor.getType() == Sensor.TYPE_HEAD_TRACKER) {
                 final int lastCount = mSensorDiscontinuityCounts.get(handle);
                 final int curCount = Float.floatToIntBits(values[6]);
                 if (lastCount >= 0 && lastCount != curCount) {
-                    ((SensorEventCallback) mListener).onSensorDiscontinuity(t.sensor);
+                    mSensorDiscontinuityCounts.put(handle, curCount);
+                    t.firstEventAfterDiscontinuity = true;
                 }
             }
 
diff --git a/core/java/android/os/logcat/ILogcatManagerService.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl
index 02db274..29b4570 100644
--- a/core/java/android/os/logcat/ILogcatManagerService.aidl
+++ b/core/java/android/os/logcat/ILogcatManagerService.aidl
@@ -19,10 +19,54 @@
 /**
  * @hide
  */
-interface ILogcatManagerService {
+oneway interface ILogcatManagerService {
+    /**
+     * The function is called by logd to notify LogcatManagerService
+     * that a client makes privileged log data access request.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void startThread(in int uid, in int gid, in int pid, in int fd);
+
+
+    /**
+     * The function is called by logd to notify LogcatManagerService
+     * that a client finished the privileged log data access.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void finishThread(in int uid, in int gid, in int pid, in int fd);
+
+
+    /**
+     * The function is called by UX component to notify
+     * LogcatManagerService that the user approved
+     * the privileged log data access.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void approve(in int uid, in int gid, in int pid, in int fd);
+
+
+    /**
+     * The function is called by UX component to notify
+     * LogcatManagerService that the user declined
+     * the privileged log data access.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void decline(in int uid, in int gid, in int pid, in int fd);
 }
 
diff --git a/core/java/android/service/attention/AttentionService.java b/core/java/android/service/attention/AttentionService.java
index f5c59b5..462ac14 100644
--- a/core/java/android/service/attention/AttentionService.java
+++ b/core/java/android/service/attention/AttentionService.java
@@ -128,9 +128,9 @@
 
         /** {@inheritDoc} */
         @Override
-        public void onStartProximityUpdates(IProximityCallback callback) {
+        public void onStartProximityUpdates(IProximityUpdateCallback callback) {
             Objects.requireNonNull(callback);
-            AttentionService.this.onStartProximityUpdates(new ProximityCallback(callback));
+            AttentionService.this.onStartProximityUpdates(new ProximityUpdateCallback(callback));
 
         }
 
@@ -166,11 +166,11 @@
 
     /**
      * Requests the continuous updates of proximity signal via the provided callback,
-     * until the given callback is unregistered.
+     * until {@link #onStopProximityUpdates} is called.
      *
      * @param callback the callback to return the result to
      */
-    public void onStartProximityUpdates(@NonNull ProximityCallback callback) {
+    public void onStartProximityUpdates(@NonNull ProximityUpdateCallback callback) {
         Slog.w(LOG_TAG, "Override this method.");
     }
 
@@ -213,22 +213,24 @@
         }
     }
 
-    /** Callbacks for ProximityCallback results. */
-    public static final class ProximityCallback {
-        @NonNull private final WeakReference<IProximityCallback> mCallback;
+    /** Callbacks for ProximityUpdateCallback results. */
+    public static final class ProximityUpdateCallback {
+        @NonNull private final WeakReference<IProximityUpdateCallback> mCallback;
 
-        private ProximityCallback(@NonNull IProximityCallback callback) {
+        private ProximityUpdateCallback(@NonNull IProximityUpdateCallback callback) {
             mCallback = new WeakReference<>(callback);
         }
 
         /**
          * @param distance the estimated distance of the user (in meter)
-         * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
-         *
+         * The distance will be {@link #PROXIMITY_UNKNOWN} if the proximity sensing
+         * was inconclusive.
          */
         public void onProximityUpdate(double distance) {
             try {
-                mCallback.get().onProximityUpdate(distance);
+                if (mCallback.get() != null) {
+                    mCallback.get().onProximityUpdate(distance);
+                }
             } catch (RemoteException e) {
                 e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/service/attention/IAttentionService.aidl b/core/java/android/service/attention/IAttentionService.aidl
index 8bb881b..e3ce114 100644
--- a/core/java/android/service/attention/IAttentionService.aidl
+++ b/core/java/android/service/attention/IAttentionService.aidl
@@ -17,7 +17,7 @@
 package android.service.attention;
 
 import android.service.attention.IAttentionCallback;
-import android.service.attention.IProximityCallback;
+import android.service.attention.IProximityUpdateCallback;
 
 /**
  * Interface for a concrete implementation to provide to the AttentionManagerService.
@@ -27,6 +27,6 @@
 oneway interface IAttentionService {
     void checkAttention(IAttentionCallback callback);
     void cancelAttentionCheck(IAttentionCallback callback);
-    void onStartProximityUpdates(IProximityCallback callback);
+    void onStartProximityUpdates(IProximityUpdateCallback callback);
     void onStopProximityUpdates();
 }
\ No newline at end of file
diff --git a/core/java/android/service/attention/IProximityCallback.aidl b/core/java/android/service/attention/IProximityUpdateCallback.aidl
similarity index 77%
rename from core/java/android/service/attention/IProximityCallback.aidl
rename to core/java/android/service/attention/IProximityUpdateCallback.aidl
index 9ecf9bc..26daa5c 100644
--- a/core/java/android/service/attention/IProximityCallback.aidl
+++ b/core/java/android/service/attention/IProximityUpdateCallback.aidl
@@ -5,6 +5,6 @@
  *
  * @hide
  */
-oneway interface IProximityCallback {
+oneway interface IProximityUpdateCallback {
     void onProximityUpdate(double distance);
 }
diff --git a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
index 179d39d..9a11923 100644
--- a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
+++ b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
@@ -1318,6 +1318,7 @@
         contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
         contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
+        contentContainer.setContentDescription(FloatingToolbar.FLOATING_TOOLBAR_TAG);
         contentContainer.setClipToOutline(true);
         return contentContainer;
     }
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 22cffc1..6a65efb 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -199,7 +199,7 @@
     }
 
     private void dispatchTriggerModelDownload(Intent intent) {
-        RecognitionService.this.triggerModelDownload(intent);
+        RecognitionService.this.onTriggerModelDownload(intent);
     }
 
     private class StartListeningArgs {
@@ -283,7 +283,7 @@
     /**
      * Requests the download of the recognizer support for {@code recognizerIntent}.
      */
-    public void triggerModelDownload(@NonNull Intent recognizerIntent) {
+    public void onTriggerModelDownload(@NonNull Intent recognizerIntent) {
         if (DBG) {
             Log.i(TAG, String.format("#downloadModel [%s]", recognizerIntent));
         }
diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
index ff04825..c5ab82d 100644
--- a/core/java/android/util/PackageUtils.java
+++ b/core/java/android/util/PackageUtils.java
@@ -18,13 +18,17 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.content.pm.Signature;
 import android.text.TextUtils;
 
 import libcore.util.HexEncoding;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.security.DigestInputStream;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
@@ -35,6 +39,9 @@
  */
 public final class PackageUtils {
 
+    private static final int LOW_RAM_BUFFER_SIZE_BYTES = 1 * 1000;            // 1 kB
+    private static final int HIGH_RAM_BUFFER_SIZE_BYTES = 1 * 1000 * 1000;    // 1 MB
+
     private PackageUtils() {
         /* hide constructor */
     }
@@ -162,4 +169,55 @@
 
         return TextUtils.join(separator, pieces);
     }
+
+    /**
+     * @see #computeSha256DigestForLargeFile(String, String)
+     */
+    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath) {
+        return computeSha256DigestForLargeFile(filePath, null);
+    }
+
+    /**
+     * Computes the SHA256 digest of large files.
+     * @param filePath The path to which the file's content is to be hashed.
+     * @param separator Separator between each pair of characters, such as colon, or null to omit.
+     * @return The digest or null if an error occurs.
+     */
+    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath,
+            @Nullable String separator) {
+        MessageDigest messageDigest;
+        try {
+            messageDigest = MessageDigest.getInstance("SHA256");
+            messageDigest.reset();
+        } catch (NoSuchAlgorithmException e) {
+            // this shouldn't happen!
+            return null;
+        }
+
+        boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
+        int bufferSize = isLowRamDevice ? LOW_RAM_BUFFER_SIZE_BYTES : HIGH_RAM_BUFFER_SIZE_BYTES;
+
+        File f = new File(filePath);
+        try {
+            DigestInputStream digestStream = new DigestInputStream(new FileInputStream(f),
+                    messageDigest);
+            byte[] buffer = new byte[bufferSize];
+            while (digestStream.read(buffer) != -1);
+        } catch (IOException e) {
+            return null;
+        }
+
+        byte[] resultBytes = messageDigest.digest();
+
+        if (separator == null) {
+            return HexEncoding.encodeToString(resultBytes, true);
+        }
+
+        int length = resultBytes.length;
+        String[] pieces = new String[length];
+        for (int index = 0; index < length; index++) {
+            pieces[index] = HexEncoding.encodeToString(resultBytes[index], true);
+        }
+        return TextUtils.join(separator, pieces);
+    }
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index c08177e..98cef958 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -389,9 +389,7 @@
         public static final int JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED = 0x4;
         // Either App or GPU took too long on the frame
         public static final int JANK_APP_DEADLINE_MISSED = 0x8;
-        // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
-        // jank
-        // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
+        // Vsync predictions have drifted beyond the threshold from the actual HWVsync
         public static final int PREDICTION_ERROR = 0x10;
         // Latching a buffer early might cause an early present of the frame
         public static final int SURFACE_FLINGER_SCHEDULING = 0x20;
diff --git a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
index 6de0316..ef04b2c 100644
--- a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
+++ b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
@@ -105,7 +105,7 @@
 
     private boolean isRemoteSelectionToolbarEnabled() {
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SELECTION_TOOLBAR,
-                REMOTE_SELECTION_TOOLBAR_ENABLED, false);
+                REMOTE_SELECTION_TOOLBAR_ENABLED, true);
     }
 
     /**
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index aaaf729..7718a3b 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -364,8 +364,13 @@
         private final Point mEndRelOffset = new Point();
         private ActivityManager.RunningTaskInfo mTaskInfo = null;
         private boolean mAllowEnterPip;
-        private int mStartRotation = ROTATION_UNDEFINED;
-        private int mEndRotation = ROTATION_UNDEFINED;
+        private @Surface.Rotation int mStartRotation = ROTATION_UNDEFINED;
+        private @Surface.Rotation int mEndRotation = ROTATION_UNDEFINED;
+        /**
+         * The end rotation of the top activity after fixed rotation is finished. If the top
+         * activity is not in fixed rotation, it will be {@link ROTATION_UNDEFINED}.
+         */
+        private @Surface.Rotation int mEndFixedRotation = ROTATION_UNDEFINED;
         private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
         private @ColorInt int mBackgroundColor;
 
@@ -388,6 +393,7 @@
             mAllowEnterPip = in.readBoolean();
             mStartRotation = in.readInt();
             mEndRotation = in.readInt();
+            mEndFixedRotation = in.readInt();
             mRotationAnimation = in.readInt();
             mBackgroundColor = in.readInt();
         }
@@ -441,6 +447,11 @@
             mEndRotation = end;
         }
 
+        /** Sets end rotation that top activity will be launched to after fixed rotation. */
+        public void setEndFixedRotation(@Surface.Rotation int endFixedRotation) {
+            mEndFixedRotation = endFixedRotation;
+        }
+
         /**
          * Sets the app-requested animation type for rotation. Will be one of the
          * ROTATION_ANIMATION_ values in {@link android.view.WindowManager.LayoutParams};
@@ -521,14 +532,21 @@
             return mAllowEnterPip;
         }
 
+        @Surface.Rotation
         public int getStartRotation() {
             return mStartRotation;
         }
 
+        @Surface.Rotation
         public int getEndRotation() {
             return mEndRotation;
         }
 
+        @Surface.Rotation
+        public int getEndFixedRotation() {
+            return mEndFixedRotation;
+        }
+
         /** @return the rotation animation. */
         public int getRotationAnimation() {
             return mRotationAnimation;
@@ -555,6 +573,7 @@
             dest.writeBoolean(mAllowEnterPip);
             dest.writeInt(mStartRotation);
             dest.writeInt(mEndRotation);
+            dest.writeInt(mEndFixedRotation);
             dest.writeInt(mRotationAnimation);
             dest.writeInt(mBackgroundColor);
         }
@@ -584,7 +603,8 @@
             return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
                     + " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
                     + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r="
-                    + mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation + "}";
+                    + mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation
+                    + " endFixedRotation=" + mEndFixedRotation + "}";
         }
     }
 
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 17b675f..5007df5 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -16,16 +16,22 @@
 
 package android.window;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.util.Log;
 import android.view.IWindowManager;
 import android.view.WindowManager.LayoutParams.WindowType;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.annotation.Retention;
+
 /**
  * The controller to manage {@link WindowContext}, such as attaching to a window manager node or
  * detaching from the current attached node. The user must call
@@ -35,13 +41,43 @@
  * @hide
  */
 public class WindowContextController {
+    // TODO(220049234): Disable attach debug logging before shipping.
+    private static final boolean DEBUG_ATTACH = true;
+    private static final String TAG = "WindowContextController";
+
     /**
-     * {@code true} to indicate that the {@code mToken} is associated with a
+     * {@link AttachStatus.STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a
      * {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
-     * WindowToken after this flag sets to {@code true}.
+     * WindowToken after this flag sets to {@link AttachStatus.STATUS_ATTACHED}.
      */
     @VisibleForTesting
-    public boolean mAttachedToDisplayArea;
+    public int mAttachedToDisplayArea = AttachStatus.STATUS_INITIALIZED;
+
+    /**
+     * Status to indicate that the Window Context attach to a
+     * {@link com.android.server.wm.DisplayArea}.
+     */
+    @Retention(SOURCE)
+    @IntDef({AttachStatus.STATUS_INITIALIZED, AttachStatus.STATUS_ATTACHED,
+            AttachStatus.STATUS_DETACHED, AttachStatus.STATUS_FAILED})
+    public @interface AttachStatus{
+        /**
+         * The Window Context haven't attached to a {@link com.android.server.wm.DisplayArea}.
+         */
+        int STATUS_INITIALIZED = 0;
+        /**
+         * The Window Context has already attached to a {@link com.android.server.wm.DisplayArea}.
+         */
+        int STATUS_ATTACHED = 1;
+        /**
+         * The Window Context has detached from a {@link com.android.server.wm.DisplayArea}.
+         */
+        int STATUS_DETACHED = 2;
+        /**
+         * The Window Context fails to attach to a {@link com.android.server.wm.DisplayArea}.
+         */
+        int STATUS_FAILED = 3;
+    }
     @NonNull
     private final WindowTokenClient mToken;
 
@@ -65,11 +101,19 @@
      * DisplayArea.
      */
     public void attachToDisplayArea(@WindowType int type, int displayId, @Nullable Bundle options) {
-        if (mAttachedToDisplayArea) {
+        if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) {
             throw new IllegalStateException("A Window Context can be only attached to "
                     + "a DisplayArea once.");
         }
-        mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options);
+        mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options)
+                ? AttachStatus.STATUS_ATTACHED : AttachStatus.STATUS_FAILED;
+        if (mAttachedToDisplayArea == AttachStatus.STATUS_FAILED) {
+            Log.w(TAG, "attachToDisplayArea fail, type:" + type + ", displayId:"
+                    + displayId);
+        } else if (DEBUG_ATTACH) {
+            Log.d(TAG, "attachToDisplayArea success, type:" + type + ", displayId:"
+                    + displayId);
+        }
     }
 
     /**
@@ -93,18 +137,21 @@
      * @see IWindowManager#attachWindowContextToWindowToken(IBinder, IBinder)
      */
     public void attachToWindowToken(IBinder windowToken) {
-        if (!mAttachedToDisplayArea) {
+        if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) {
             throw new IllegalStateException("The Window Context should have been attached"
-                    + " to a DisplayArea.");
+                    + " to a DisplayArea. AttachToDisplayArea:" + mAttachedToDisplayArea);
         }
         mToken.attachToWindowToken(windowToken);
     }
 
     /** Detaches the window context from the node it's currently associated with. */
     public void detachIfNeeded() {
-        if (mAttachedToDisplayArea) {
+        if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) {
             mToken.detachFromWindowContainerIfNeeded();
-            mAttachedToDisplayArea = false;
+            mAttachedToDisplayArea = AttachStatus.STATUS_DETACHED;
+            if (DEBUG_ATTACH) {
+                Log.d(TAG, "Detach Window Context.");
+            }
         }
     }
 }
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index e1a67d8..34c47ed 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -98,6 +98,7 @@
     private final Handler mHandler;
     private final ChoreographerWrapper mChoreographer;
     private final Object mLock = InteractionJankMonitor.getInstance().getLock();
+    private final boolean mDeferMonitoring;
 
     @VisibleForTesting
     public final boolean mSurfaceOnly;
@@ -153,6 +154,7 @@
         mHandler = handler;
         mChoreographer = choreographer;
         mSurfaceControlWrapper = surfaceControlWrapper;
+        mDeferMonitoring = config.shouldDeferMonitor();
 
         // HWUI instrumentation init.
         mRendererWrapper = mSurfaceOnly ? null : renderer;
@@ -228,12 +230,25 @@
      */
     public void begin() {
         synchronized (mLock) {
-            mBeginVsyncId = mChoreographer.getVsyncId() + 1;
+            final long currentVsync = mChoreographer.getVsyncId();
+            // In normal case, we should begin at the next frame,
+            // the id of the next frame is not simply increased by 1,
+            // but we can exclude the current frame at least.
+            mBeginVsyncId = mDeferMonitoring ? currentVsync + 1 : currentVsync;
             if (DEBUG) {
-                Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId);
+                Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId
+                        + ", defer=" + mDeferMonitoring);
             }
             if (mSurfaceControl != null) {
-                postTraceStartMarker();
+                if (mDeferMonitoring) {
+                    // Normal case, we begin the instrument from the very beginning,
+                    // except the first frame.
+                    postTraceStartMarker();
+                } else {
+                    // If we don't begin the instrument from the very beginning,
+                    // there is no need to skip the frame where the begin invocation happens.
+                    beginInternal();
+                }
                 mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
             }
             if (!mSurfaceOnly) {
@@ -247,15 +262,18 @@
      */
     @VisibleForTesting
     public void postTraceStartMarker() {
-        mChoreographer.mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, () -> {
-            synchronized (mLock) {
-                if (mCancelled || mEndVsyncId != INVALID_ID) {
-                    return;
-                }
-                mTracingStarted = true;
-                Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+        mChoreographer.mChoreographer.postCallback(
+                Choreographer.CALLBACK_INPUT, this::beginInternal, null);
+    }
+
+    private void beginInternal() {
+        synchronized (mLock) {
+            if (mCancelled || mEndVsyncId != INVALID_ID) {
+                return;
             }
-        }, null);
+            mTracingStarted = true;
+            Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+        }
     }
 
     /**
@@ -464,8 +482,7 @@
             if (info.surfaceControlCallbackFired) {
                 totalFramesCount++;
                 boolean missedFrame = false;
-                if ((info.jankType & PREDICTION_ERROR) != 0
-                        || ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0)) {
+                if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
                     Log.w(TAG, "Missed App frame:" + info.jankType);
                     missedAppFramesCount++;
                     missedFrame = true;
@@ -473,7 +490,8 @@
                 if ((info.jankType & DISPLAY_HAL) != 0
                         || (info.jankType & JANK_SURFACEFLINGER_DEADLINE_MISSED) != 0
                         || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
-                        || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0) {
+                        || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
+                        || (info.jankType & PREDICTION_ERROR) != 0) {
                     Log.w(TAG, "Missed SF frame:" + info.jankType);
                     missedSfFramesCount++;
                     missedFrame = true;
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 5a66e9a..3746bfd 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -717,6 +717,7 @@
         private final boolean mSurfaceOnly;
         private final SurfaceControl mSurfaceControl;
         private final @CujType int mCujType;
+        private final boolean mDeferMonitor;
 
         /**
          * A builder for building Configuration. {@link #setView(View)} is essential
@@ -733,6 +734,7 @@
             private boolean mAttrSurfaceOnly;
             private SurfaceControl mAttrSurfaceControl;
             private @CujType int mAttrCujType;
+            private boolean mAttrDeferMonitor = true;
 
             /**
              * Creates a builder which instruments only surface.
@@ -823,6 +825,16 @@
             }
 
             /**
+             * Indicates if the instrument should be deferred to the next frame.
+             * @param defer true if the instrument should be deferred to the next frame.
+             * @return builder
+             */
+            public Builder setDeferMonitorForAnimationStart(boolean defer) {
+                mAttrDeferMonitor = defer;
+                return this;
+            }
+
+            /**
              * Builds the {@link Configuration} instance
              * @return the instance of {@link Configuration}
              * @throws IllegalArgumentException if any invalid attribute is set
@@ -830,12 +842,14 @@
             public Configuration build() throws IllegalArgumentException {
                 return new Configuration(
                         mAttrCujType, mAttrView, mAttrTag, mAttrTimeout,
-                        mAttrSurfaceOnly, mAttrContext, mAttrSurfaceControl);
+                        mAttrSurfaceOnly, mAttrContext, mAttrSurfaceControl,
+                        mAttrDeferMonitor);
             }
         }
 
         private Configuration(@CujType int cuj, View view, String tag, long timeout,
-                boolean surfaceOnly, Context context, SurfaceControl surfaceControl) {
+                boolean surfaceOnly, Context context, SurfaceControl surfaceControl,
+                boolean deferMonitor) {
             mCujType = cuj;
             mTag = tag;
             mTimeout = timeout;
@@ -845,6 +859,7 @@
                     ? context
                     : (view != null ? view.getContext().getApplicationContext() : null);
             mSurfaceControl = surfaceControl;
+            mDeferMonitor = deferMonitor;
             validate();
         }
 
@@ -901,6 +916,13 @@
         Context getContext() {
             return mContext;
         }
+
+        /**
+         * @return true if the monitoring should be deferred to the next frame, false otherwise.
+         */
+        public boolean shouldDeferMonitor() {
+            return mDeferMonitor;
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
index 80d8bd7..8c61a12 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -1475,6 +1475,7 @@
         contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
         contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
+        contentContainer.setContentDescription(FloatingToolbar.FLOATING_TOOLBAR_TAG);
         contentContainer.setClipToOutline(true);
         return contentContainer;
     }
diff --git a/core/res/res/anim/popup_enter_material.xml b/core/res/res/anim/popup_enter_material.xml
index ef5b7c0..9f771b9 100644
--- a/core/res/res/anim/popup_enter_material.xml
+++ b/core/res/res/anim/popup_enter_material.xml
@@ -22,7 +22,7 @@
         android:interpolator="@interpolator/standard"
         android:duration="@android:integer/config_activityDefaultDur" />
     <translate
-        android:fromYDelta="20dp"
+        android:fromYDelta="@android:dimen/popup_enter_animation_from_y_delta"
         android:toYDelta="0"
         android:interpolator="@interpolator/standard"
         android:duration="@android:integer/config_activityDefaultDur" />
diff --git a/core/res/res/anim/popup_exit_material.xml b/core/res/res/anim/popup_exit_material.xml
index 1efa702..2b79ddf 100644
--- a/core/res/res/anim/popup_exit_material.xml
+++ b/core/res/res/anim/popup_exit_material.xml
@@ -23,7 +23,7 @@
         android:duration="@android:integer/config_activityShortDur" />
     <translate
         android:fromYDelta="0"
-        android:toYDelta="-10dp"
+        android:toYDelta="@android:dimen/popup_exit_animation_to_y_delta"
         android:interpolator="@interpolator/standard_accelerate"
         android:duration="@android:integer/config_activityShortDur" />
 </set>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 39b41b5..744c3dab 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -994,4 +994,8 @@
 
     <!-- Default size for user icons (a.k.a. avatar images) -->
     <dimen name="user_icon_size">190dp</dimen>
+
+    <!-- Dimensions for the translations of the default dialog animation. -->
+    <dimen name="popup_enter_animation_from_y_delta">20dp</dimen>
+    <dimen name="popup_exit_animation_to_y_delta">-10dp</dimen>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f531c3a..e7eeecc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2358,6 +2358,10 @@
   <java-symbol type="string" name="nas_upgrade_notification_learn_more_action" />
   <java-symbol type="string" name="nas_upgrade_notification_learn_more_content" />
   <java-symbol type="bool" name="config_settingsHelpLinksEnabled" />
+  <java-symbol type="integer" name="config_activityDefaultDur" />
+  <java-symbol type="integer" name="config_activityShortDur" />
+  <java-symbol type="dimen" name="popup_enter_animation_from_y_delta" />
+  <java-symbol type="dimen" name="popup_exit_animation_to_y_delta" />
 
   <!-- ImfTest -->
   <java-symbol type="layout" name="auto_complete_list" />
diff --git a/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java b/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java
new file mode 100644
index 0000000..c6f5924
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.widget;
+
+import static com.android.internal.widget.floatingtoolbar.FloatingToolbar.FLOATING_TOOLBAR_TAG;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.res.Resources;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.R;
+
+final class FloatingToolbarUtils {
+
+    private final UiDevice mDevice;
+
+    FloatingToolbarUtils() {
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+    }
+
+    void waitForFloatingToolbarPopup() {
+        mDevice.wait(Until.findObject(By.desc(FLOATING_TOOLBAR_TAG)), 500);
+    }
+
+    void assertFloatingToolbarIsDisplayed() {
+        waitForFloatingToolbarPopup();
+        assertThat(mDevice.hasObject(By.desc(FLOATING_TOOLBAR_TAG))).isTrue();
+    }
+
+    void assertFloatingToolbarContainsItem(String itemLabel) {
+        waitForFloatingToolbarPopup();
+        assertWithMessage("Expected to find item labelled [" + itemLabel + "]")
+                .that(mDevice.hasObject(
+                        By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(itemLabel))))
+                .isTrue();
+    }
+
+    void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
+        waitForFloatingToolbarPopup();
+        assertWithMessage("Expected to not find item labelled [" + itemLabel + "]")
+                .that(mDevice.hasObject(
+                        By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(itemLabel))))
+                .isFalse();
+    }
+
+    void assertFloatingToolbarContainsItemAtIndex(String itemLabel, int index) {
+        waitForFloatingToolbarPopup();
+        assertWithMessage("Expected to find item labelled [" + itemLabel + "] at index " + index)
+                .that(mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+                        .findObjects(By.clickable(true))
+                        .get(index)
+                        .getChildren()
+                        .get(1)
+                        .getText())
+                .isEqualTo(itemLabel);
+    }
+
+    void clickFloatingToolbarItem(String label) {
+        waitForFloatingToolbarPopup();
+        mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+                .findObject(By.text(label))
+                .click();
+    }
+
+    void clickFloatingToolbarOverflowItem(String label) {
+        // TODO: There might be a benefit to combining this with "clickFloatingToolbarItem" method.
+        waitForFloatingToolbarPopup();
+        mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+                .findObject(By.desc(str(R.string.floating_toolbar_open_overflow_description)))
+                .click();
+        mDevice.wait(
+                Until.findObject(By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(label))),
+                1000);
+        mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+                .findObject(By.text(label))
+                .click();
+    }
+
+    private static String str(int id) {
+        return Resources.getSystem().getString(id);
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index 28f9ccc..90844ea 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -17,9 +17,6 @@
 package android.widget;
 
 import static android.widget.espresso.DragHandleUtils.onHandleView;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
 import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupContainsItem;
 import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsDisplayed;
 import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsNotDisplayed;
@@ -72,6 +69,7 @@
     @Rule
     public final ActivityTestRule<TextViewActivity> mActivityRule =
             new ActivityTestRule<>(TextViewActivity.class);
+    private final FloatingToolbarUtils mToolbar = new FloatingToolbarUtils();
 
     private TextViewActivity getActivity() {
         return mActivityRule.getActivity();
@@ -118,22 +116,19 @@
         setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarContainsItem(
-                getActivity().getString(com.android.internal.R.string.replace));
-        sleepForFloatingToolbarPopup();
-        clickFloatingToolbarItem(
+        mToolbar.clickFloatingToolbarOverflowItem(
                 getActivity().getString(com.android.internal.R.string.replace));
 
         assertSuggestionsPopupIsDisplayed();
     }
 
     @Test
-    public void testInsertionActionMode() {
+    public void testInsertionActionMode() throws Throwable {
         final String text = "abc def ghi";
 
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(replaceText(text));
+        Thread.sleep(500);
 
         final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
                 new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
@@ -141,10 +136,7 @@
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e')));
         onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarContainsItem(
-                getActivity().getString(com.android.internal.R.string.replace));
-        clickFloatingToolbarItem(
+        mToolbar.clickFloatingToolbarItem(
                 getActivity().getString(com.android.internal.R.string.replace));
 
         assertSuggestionsPopupIsDisplayed();
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 152992c..659cd98 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,15 +16,10 @@
 
 package android.widget;
 
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
 import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
 import static android.widget.espresso.DragHandleUtils.onHandleView;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
 import static android.widget.espresso.TextViewActions.Handle;
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
@@ -64,10 +59,17 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
 import android.content.ClipData;
 import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
 import android.os.Bundle;
+import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
 import android.text.InputType;
 import android.text.Selection;
 import android.text.Spannable;
@@ -79,6 +81,7 @@
 import android.view.MenuItem;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.textclassifier.SelectionEvent;
+import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
@@ -102,6 +105,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Tests the TextView widget from an Activity
@@ -116,11 +120,16 @@
 
     private Activity mActivity;
     private Instrumentation mInstrumentation;
+    private UiDevice mDevice;
+    private FloatingToolbarUtils mToolbar;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         mActivity = mActivityRule.getActivity();
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mDevice = UiDevice.getInstance(mInstrumentation);
+        mDevice.wakeUp();
+        mToolbar = new FloatingToolbarUtils();
         TextClassificationManager tcm = mActivity.getSystemService(
                 TextClassificationManager.class);
         tcm.setTextClassifier(TextClassifier.NO_OP);
@@ -132,14 +141,14 @@
         final String helloWorld = "Hello world!";
         // We use replaceText instead of typeTextIntoFocusedView to input text to avoid
         // unintentional interactions with software keyboard.
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
 
         onView(withId(R.id.textview)).check(matches(withText(helloWorld)));
     }
     @Test
     public void testPositionCursorAtTextAtIndex() {
         final String helloWorld = "Hello world!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world")));
 
         // Delete text at specified index and see if we got the right one.
@@ -152,7 +161,7 @@
         // Arabic text. The expected cursorable boundary is
         // | \u0623 \u064F | \u067A | \u0633 \u0652 |
         final String text = "\u0623\u064F\u067A\u0633\u0652";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
@@ -172,7 +181,7 @@
     public void testPositionCursorAtTextAtIndex_devanagari() {
         // Devanagari text. The expected cursorable boundary is | \u0915 \u093E |
         final String text = "\u0915\u093E";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
@@ -186,7 +195,7 @@
     public void testLongPressToSelect() {
         final String helloWorld = "Hello Kirk!";
         onView(withId(R.id.textview)).perform(click());
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(
                 longPressOnTextAtIndex(helloWorld.indexOf("Kirk")));
 
@@ -196,7 +205,7 @@
     @Test
     public void testLongPressEmptySpace() {
         final String helloWorld = "Hello big round sun!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         // Move cursor somewhere else
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("big")));
         // Long-press at end of line.
@@ -210,7 +219,7 @@
     @Test
     public void testLongPressAndDragToSelect() {
         final String helloWorld = "Hello little handsome boy!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(
                 longPressAndDragOnText(helloWorld.indexOf("little"), helloWorld.indexOf(" boy!")));
 
@@ -220,7 +229,7 @@
     @Test
     public void testLongPressAndDragToSelect_emoji() {
         final String text = "\uD83D\uDE00\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(longPressAndDragOnText(4, 6));
         onView(withId(R.id.textview)).check(hasSelection("\uD83D\uDE02"));
@@ -234,7 +243,7 @@
     @Test
     public void testDragAndDrop() {
         final String text = "abc def ghi.";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("e")));
 
         onView(withId(R.id.textview)).perform(
@@ -254,7 +263,7 @@
     @Test
     public void testDoubleTapToSelect() {
         final String helloWorld = "Hello SuetYi!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
 
         onView(withId(R.id.textview)).perform(
                 doubleClickOnTextAtIndex(helloWorld.indexOf("SuetYi")));
@@ -265,7 +274,7 @@
     @Test
     public void testDoubleTapAndDragToSelect() {
         final String helloWorld = "Hello young beautiful person!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(doubleTapAndDragOnText(helloWorld.indexOf("young"),
                         helloWorld.indexOf(" person!")));
 
@@ -275,7 +284,7 @@
     @Test
     public void testDoubleTapAndDragToSelect_multiLine() {
         final String helloWorld = "abcd\n" + "efg\n" + "hijklm\n" + "nop";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(
                 doubleTapAndDragOnText(helloWorld.indexOf("m"), helloWorld.indexOf("a")));
         onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijklm"));
@@ -284,7 +293,7 @@
     @Test
     public void testSelectBackwordsByTouch() {
         final String helloWorld = "Hello king of the Jungle!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(
                 doubleTapAndDragOnText(helloWorld.indexOf(" Jungle!"), helloWorld.indexOf("king")));
 
@@ -294,12 +303,11 @@
     @Test
     public void testToolbarAppearsAfterSelection() {
         final String text = "Toolbar appears after selection.";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(
                 longPressOnTextAtIndex(text.indexOf("appears")));
 
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
     }
 
     @Test
@@ -317,13 +325,12 @@
         });
         mInstrumentation.waitForIdleSync();
 
-        onView(withId(R.id.textview)).perform(replaceText("test"));
+        setText("test");
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1));
-        clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.cut));
+        mToolbar.clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.cut));
         onView(withId(R.id.textview)).perform(longClick());
-        sleepForFloatingToolbarPopup();
 
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
     }
 
     @Test
@@ -331,8 +338,7 @@
         TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.textview);
         int position = (textLink.getStart() + textLink.getEnd()) / 2;
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(position));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
     }
 
     @Test
@@ -342,23 +348,20 @@
         final int position = (textLink.getStart() + textLink.getEnd()) / 2;
 
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         assertTrue(textView.hasSelection());
 
         // toggle
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
-        sleepForFloatingToolbarPopup();
+        mToolbar.waitForFloatingToolbarPopup();
         assertFalse(textView.hasSelection());
 
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         assertTrue(textView.hasSelection());
 
         // click outside
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(0));
-        sleepForFloatingToolbarPopup();
         assertFalse(textView.hasSelection());
     }
 
@@ -372,8 +375,7 @@
         });
         mInstrumentation.waitForIdleSync();
 
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
     }
 
     @Test
@@ -385,7 +387,7 @@
             final TextView textView = mActivity.findViewById(R.id.textview);
             textView.setText(text);
             textView.setCustomSelectionActionModeCallback(
-                    new ActionMode.Callback() {
+                    new ActionModeCallbackAdapter() {
                         @Override
                         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                             menu.clear();
@@ -398,29 +400,19 @@
                             clickedItem[0] = item;
                             return true;
                         }
-
-                        @Override
-                        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-                            return true;
-                        }
-
-                        @Override
-                        public void onDestroyActionMode(ActionMode mode) {}
                     });
         });
         mInstrumentation.waitForIdleSync();
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f")));
-        sleepForFloatingToolbarPopup();
 
         // Change the selection so that the menu items are refreshed.
         final TextView textView = mActivity.findViewById(R.id.textview);
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, 0));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
 
-        clickFloatingToolbarItem("Item");
+        mToolbar.clickFloatingToolbarItem("Item");
         mInstrumentation.waitForIdleSync();
 
         assertEquals(latestItem[0], clickedItem[0]);
@@ -469,13 +461,11 @@
         mActivityRule.runOnUiThread(() -> textView.setFocusableInTouchMode(true));
 
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         assertTrue(textView.hasSelection());
 
         mActivityRule.runOnUiThread(() -> textView.clearFocus());
         mInstrumentation.waitForIdleSync();
-        sleepForFloatingToolbarPopup();
 
         assertFalse(textView.hasSelection());
     }
@@ -488,14 +478,12 @@
 
         onView(withId(R.id.nonselectable_textview))
                 .perform(clickOnTextAtIndex(nonselectablePosition));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         assertTrue(nonselectableTextView.hasSelection());
 
-        UiDevice device = UiDevice.getInstance(mInstrumentation);
-        device.openNotification();
+        mDevice.openNotification();
         Thread.sleep(2000);
-        device.pressBack();
+        mDevice.pressBack();
         Thread.sleep(2000);
 
         assertFalse(nonselectableTextView.hasSelection());
@@ -528,62 +516,58 @@
     }
 
     @Test
-    public void testToolbarAndInsertionHandle() {
+    public void testToolbarAndInsertionHandle() throws Throwable {
         final String text = "text";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
+        Thread.sleep(500);
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
 
         onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
 
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.selectAll));
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.copy));
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.cut));
     }
 
     @Test
     public void testToolbarAndSelectionHandle() {
         final String text = "abcd efg hijk";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f")));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
 
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.selectAll));
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.copy));
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.cut));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
 
         onHandleView(com.android.internal.R.id.selection_end_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_END, text.length()));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
 
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.selectAll));
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.copy));
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.cut));
     }
 
     @Test
     public void testInsertionHandle() {
         final String text = "abcd efg hijk ";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -602,7 +586,7 @@
     @Test
     public void testInsertionHandle_multiLine() {
         final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -640,7 +624,7 @@
         final TextView textView = mActivity.findViewById(R.id.textview);
 
         final String text = "hello the world";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -654,7 +638,7 @@
         enableFlagsForInsertionHandleGestures();
         final TextView textView = mActivity.findViewById(R.id.textview);
         final String text = "hello the world";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -670,7 +654,7 @@
         final TextView textView = mActivity.findViewById(R.id.textview);
 
         final String text = "hello the world";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -685,7 +669,7 @@
         final TextView textView = mActivity.findViewById(R.id.textview);
 
         final String text = "hello the world";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -698,7 +682,7 @@
     @Test
     public void testSelectionHandles() {
         final String text = "abcd efg hijk lmn";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
 
@@ -720,7 +704,7 @@
     @Test
     public void testSelectionHandles_bidi() {
         final String text = "abc \u0621\u0622\u0623 def";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0622')));
 
@@ -762,7 +746,7 @@
     @Test
     public void testSelectionHandles_multiLine() {
         final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -790,7 +774,7 @@
         final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n"
                 + "\u0630\u0631\u0632\n" + "\u0633\u0634\u0635\n" + "\u0636\u0637\u0638\n"
                 + "\u0639\u063A\u063B";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0634')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -817,7 +801,7 @@
     @Test
     public void testSelectionHandles_doesNotPassAnotherHandle() {
         final String text = "abcd efg hijk lmn";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -834,7 +818,7 @@
     @Test
     public void testSelectionHandles_doesNotPassAnotherHandle_multiLine() {
         final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -851,7 +835,7 @@
     @Test
     public void testSelectionHandles_snapToWordBoundary() {
         final String text = "abcd efg hijk lmn opqr";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -904,7 +888,7 @@
     @Test
     public void testSelectionHandles_snapToWordBoundary_multiLine() {
         final String text = "abcd efg\n" + "hijk lmn\n" + "opqr stu";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('m')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -939,7 +923,7 @@
     @Test
     public void testSelectionHandles_visibleEvenWithEmptyMenu() {
         ((TextView) mActivity.findViewById(R.id.textview)).setCustomSelectionActionModeCallback(
-                new ActionMode.Callback() {
+                new ActionModeCallbackAdapter() {
                     @Override
                     public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                         menu.clear();
@@ -951,17 +935,9 @@
                         menu.clear();
                         return true;
                     }
-
-                    @Override
-                    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-                        return false;
-                    }
-
-                    @Override
-                    public void onDestroyActionMode(ActionMode mode) {}
                 });
         final String text = "abcd efg hijk lmn";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
 
@@ -982,7 +958,7 @@
         textView.setCustomSelectionActionModeCallback(amCallback);
 
         final String text = "abc def";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         mActivityRule.runOnUiThread(
                 () -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
         mInstrumentation.waitForIdleSync();
@@ -991,15 +967,13 @@
         // Make sure that "Select All" is included in the selection action mode when the entire text
         // is not selected.
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         // Changing the selection range by API should not interrupt the selection action mode.
         mActivityRule.runOnUiThread(
                 () -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
         mInstrumentation.waitForIdleSync();
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.selectAll));
         // Make sure that "Select All" is no longer included when the entire text is selected by
         // API.
@@ -1007,9 +981,8 @@
                 () -> Selection.setSelection((Spannable) textView.getText(), 0, text.length()));
         mInstrumentation.waitForIdleSync();
 
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.selectAll));
         // Make sure that shrinking the selection range to cursor (an empty range) by API
         // terminates selection action mode and does not trigger the insertion action mode.
@@ -1020,17 +993,15 @@
         // Make sure that user click can trigger the insertion action mode.
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         // Make sure that an existing insertion action mode keeps alive after the insertion point is
         // moved by API.
         mActivityRule.runOnUiThread(
                 () -> Selection.setSelection((Spannable) textView.getText(), 0));
         mInstrumentation.waitForIdleSync();
 
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.copy));
         // Make sure that selection action mode is started after selection is created by API when
         // insertion action mode is active.
@@ -1038,16 +1009,15 @@
                 () -> Selection.setSelection((Spannable) textView.getText(), 1, text.length()));
         mInstrumentation.waitForIdleSync();
 
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.copy));
     }
 
     @Test
     public void testTransientState() throws Throwable {
         final String text = "abc def";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         final TextView textView = mActivity.findViewById(R.id.textview);
         assertFalse(textView.hasTransientState());
@@ -1068,58 +1038,43 @@
 
     @Test
     public void testResetMenuItemTitle() throws Throwable {
-        mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null);
+        mActivity.getSystemService(TextClassificationManager.class)
+                .setTextClassifier(TextClassifier.NO_OP);
         final TextView textView = mActivity.findViewById(R.id.textview);
         final int itemId = 1;
-        final String title1 = " AFIGBO";
-        final int index = title1.indexOf('I');
-        final String title2 = title1.substring(index);
+        final String title1 = "@AFIGBO";
+        final int index = 3;
+        final String title2 = "IGBO";
         final String[] title = new String[]{title1};
         mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback(
-                new ActionMode.Callback() {
-                    @Override
-                    public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
-                        return true;
-                    }
-
+                new ActionModeCallbackAdapter() {
                     @Override
                     public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
-                        menu.removeItem(itemId);
+                        menu.clear();
                         menu.add(Menu.NONE /* group */, itemId, 0 /* order */, title[0]);
                         return true;
                     }
-
-                    @Override
-                    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
-                        return false;
-                    }
-
-                    @Override
-                    public void onDestroyActionMode(ActionMode actionMode) {
-                    }
                 }));
         mInstrumentation.waitForIdleSync();
 
-        onView(withId(R.id.textview)).perform(replaceText(title1));
+        setText(title1);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(index));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarContainsItem(title1);
+        mToolbar.assertFloatingToolbarContainsItem(title1);
 
         // Change the menu item title.
         title[0] = title2;
         // Change the selection to invalidate the action mode without restarting it.
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, index));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarContainsItem(title2);
+        mToolbar.assertFloatingToolbarContainsItem(title2);
     }
 
     @Test
     public void testAssistItemIsAtIndexZero() throws Throwable {
-        useSystemDefaultTextClassifier();
+        final SingleActionTextClassifier tc = useSingleActionTextClassifier();
         final TextView textView = mActivity.findViewById(R.id.textview);
         mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback(
-                new ActionMode.Callback() {
+                new ActionModeCallbackAdapter() {
                     @Override
                     public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
                         // Create another item at order position 0 to confirm that it will never be
@@ -1127,33 +1082,19 @@
                         menu.add(Menu.NONE, 0 /* id */, 0 /* order */, "Test");
                         return true;
                     }
-
-                    @Override
-                    public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
-                        return true;
-                    }
-
-                    @Override
-                    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
-                        return false;
-                    }
-
-                    @Override
-                    public void onDestroyActionMode(ActionMode actionMode) {
-                    }
                 }));
         mInstrumentation.waitForIdleSync();
         final String text = "droid@android.com";
 
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarItemIndex(android.R.id.textAssist, 0);
+        mToolbar.assertFloatingToolbarContainsItemAtIndex(tc.getActionLabel(), 0);
     }
 
     @Test
     public void testNoAssistItemForPasswordField() throws Throwable {
-        useSystemDefaultTextClassifier();
+        final SingleActionTextClassifier tc = useSingleActionTextClassifier();
+
         final TextView textView = mActivity.findViewById(R.id.textview);
         mActivityRule.runOnUiThread(() -> {
             textView.setInputType(
@@ -1162,23 +1103,22 @@
         mInstrumentation.waitForIdleSync();
         final String password = "afigbo@android.com";
 
-        onView(withId(R.id.textview)).perform(replaceText(password));
+        setText(password);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(password.indexOf('@')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
+        mToolbar.assertFloatingToolbarDoesNotContainItem(tc.getActionLabel());
     }
 
     @Test
     public void testNoAssistItemForTextFieldWithUnsupportedCharacters() throws Throwable {
-        useSystemDefaultTextClassifier();
+        // NOTE: This test addresses a security bug.
+        final SingleActionTextClassifier tc = useSingleActionTextClassifier();
         final String text = "\u202Emoc.diordna.com";
         final TextView textView = mActivity.findViewById(R.id.textview);
         mActivityRule.runOnUiThread(() -> textView.setText(text));
         mInstrumentation.waitForIdleSync();
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('.')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
+        mToolbar.assertFloatingToolbarDoesNotContainItem(tc.getActionLabel());
     }
 
     @Test
@@ -1195,10 +1135,9 @@
         mInstrumentation.waitForIdleSync();
         final String text = "andyroid@android.com";
 
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@')));
-        sleepForFloatingToolbarPopup();
-        clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
+        mToolbar.clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
         mInstrumentation.waitForIdleSync();
 
         final SelectionEvent lastEvent = selectionEvents.get(selectionEvents.size() - 1);
@@ -1214,9 +1153,8 @@
 
         final String text = "My number is 987654321";
 
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('9')));
-        sleepForFloatingToolbarPopup();
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
         mInstrumentation.waitForIdleSync();
 
@@ -1247,9 +1185,8 @@
         final String text = "My number is 987654321";
 
         // Long press to trigger selection
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('9')));
-        sleepForFloatingToolbarPopup();
 
         // Type over the selection
         onView(withId(R.id.textview)).perform(pressKey(KeyEvent.KEYCODE_A));
@@ -1291,16 +1228,14 @@
         });
 
         // Long press to trigger selection
-        onView(withId(R.id.textview)).perform(replaceText("android.com"));
+        setText("android.com");
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(0));
-        sleepForFloatingToolbarPopup();
         // Click "Copy" to dismiss the selection.
-        clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
+        mToolbar.clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
 
         // Long press to trigger another selection
-        onView(withId(R.id.textview)).perform(replaceText("android@android.com"));
+        setText("android@android.com");
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(0));
-        sleepForFloatingToolbarPopup();
 
         // suggestSelection should be called in two different TextClassifier sessions.
         assertEquals(2, testableTextClassifiers.size());
@@ -1312,10 +1247,9 @@
     public void testPastePlainText_menuAction() {
         initializeClipboardWithText(TextStyle.STYLED);
 
-        onView(withId(R.id.textview)).perform(replaceText(""));
+        setText("");
         onView(withId(R.id.textview)).perform(longClick());
-        sleepForFloatingToolbarPopup();
-        clickFloatingToolbarItem(
+        mToolbar.clickFloatingToolbarItem(
                 mActivity.getString(com.android.internal.R.string.paste_as_plain_text));
         mInstrumentation.waitForIdleSync();
 
@@ -1327,18 +1261,33 @@
     public void testPastePlainText_noMenuItemForPlainText() {
         initializeClipboardWithText(TextStyle.PLAIN);
 
-        onView(withId(R.id.textview)).perform(replaceText(""));
+        setText("");
         onView(withId(R.id.textview)).perform(longClick());
-        sleepForFloatingToolbarPopup();
 
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.paste_as_plain_text));
     }
 
+    private void setText(String text) {
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        mDevice.wait(Until.findObject(By.text(text)), 1000);
+        mInstrumentation.waitForIdleSync();
+    }
+
     private void useSystemDefaultTextClassifier() {
         mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null);
     }
 
+    private SingleActionTextClassifier useSingleActionTextClassifier() {
+        useSystemDefaultTextClassifier();
+        final TextClassificationManager tcm =
+                mActivity.getSystemService(TextClassificationManager.class);
+        final SingleActionTextClassifier oneActionTC =
+                new SingleActionTextClassifier(mActivity, tcm.getTextClassifier());
+        tcm.setTextClassifier(oneActionTC);
+        return oneActionTC;
+    }
+
     private void initializeClipboardWithText(TextStyle textStyle) {
         final ClipData clip;
         switch (textStyle) {
@@ -1360,7 +1309,7 @@
         PLAIN, STYLED
     }
 
-    private final class TestableTextClassifier implements TextClassifier {
+    private static final class TestableTextClassifier implements TextClassifier {
         final List<SelectionEvent> mSelectionEvents = new ArrayList<>();
         final List<TextSelection.Request> mTextSelectionRequests = new ArrayList<>();
 
@@ -1385,4 +1334,54 @@
             return mTextSelectionRequests;
         }
     }
+
+    private static final class SingleActionTextClassifier implements TextClassifier {
+
+        private final RemoteAction mAction;
+        private final TextClassifier mOriginal;
+        private final TextClassification mClassificationResult;
+
+        SingleActionTextClassifier(Context context, TextClassifier original) {
+            mAction = new RemoteAction(
+                    Icon.createWithResource(context, android.R.drawable.btn_star),
+                    "assist",
+                    "assist",
+                    PendingIntent.getActivity(context, 0, new Intent(), FLAG_IMMUTABLE));
+            mClassificationResult = new TextClassification.Builder().addAction(mAction).build();
+            mOriginal = Objects.requireNonNull(original);
+        }
+
+        public String getActionLabel() {
+            return mAction.getTitle().toString();
+        }
+
+        @Override
+        public TextSelection suggestSelection(TextSelection.Request request) {
+            final TextSelection sel = mOriginal.suggestSelection(request);
+            return new TextSelection.Builder(
+                    sel.getSelectionStartIndex(), sel.getSelectionEndIndex())
+                    .setTextClassification(mClassificationResult)
+                    .build();
+        }
+    }
+
+    private static class ActionModeCallbackAdapter implements ActionMode.Callback {
+        @Override
+        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
+            return true;
+        }
+
+        @Override
+        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
+            return true;
+        }
+
+        @Override
+        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
+            return true;
+        }
+
+        @Override
+        public void onDestroyActionMode(ActionMode actionMode) {}
+    }
 }
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
deleted file mode 100644
index 4f95cb8..0000000
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ /dev/null
@@ -1,263 +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
- */
-
-package android.widget.espresso;
-
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
-import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
-import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
-import static com.android.internal.widget.floatingtoolbar.LocalFloatingToolbarPopup.MenuItemRepr;
-
-import static org.hamcrest.Matchers.allOf;
-import static org.hamcrest.Matchers.is;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.test.espresso.NoMatchingRootException;
-import androidx.test.espresso.NoMatchingViewException;
-import androidx.test.espresso.UiController;
-import androidx.test.espresso.ViewAction;
-import androidx.test.espresso.ViewInteraction;
-
-import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
-
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Predicate;
-
-/**
- * Espresso utility methods for the floating toolbar.
- */
-public class FloatingToolbarEspressoUtils {
-    private final static Object TAG = FloatingToolbar.FLOATING_TOOLBAR_TAG;
-
-    private FloatingToolbarEspressoUtils() {}
-
-    private static ViewInteraction onFloatingToolBar() {
-        return onView(withTagValue(is(TAG)))
-                .inRoot(allOf(
-                        isPlatformPopup(),
-                        withDecorView(hasDescendant(withTagValue(is(TAG))))));
-    }
-
-    /**
-     * Creates a {@link ViewInteraction} for the floating bar menu item with the given matcher.
-     *
-     * @param matcher The matcher for the menu item.
-     */
-    public static ViewInteraction onFloatingToolBarItem(Matcher<View> matcher) {
-        return onView(matcher)
-                .inRoot(withDecorView(hasDescendant(withTagValue(is(TAG)))));
-    }
-
-    /**
-     * Asserts that the floating toolbar is displayed on screen.
-     *
-     * @throws AssertionError if the assertion fails
-     */
-    public static void assertFloatingToolbarIsDisplayed() {
-        onFloatingToolBar().check(matches(isDisplayed()));
-    }
-
-    /**
-     * Asserts that the floating toolbar is not displayed on screen.
-     *
-     * @throws AssertionError if the assertion fails
-     * @deprecated Negative assertions are taking too long to timeout in Espresso.
-     */
-    @Deprecated
-    public static void assertFloatingToolbarIsNotDisplayed() {
-        try {
-            onFloatingToolBar().check(matches(isDisplayed()));
-        } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
-            return;
-        }
-        throw new AssertionError("Floating toolbar is displayed");
-    }
-
-    private static void toggleOverflow() {
-        final int id = com.android.internal.R.id.overflow;
-        onView(allOf(withId(id), isDisplayed()))
-                .inRoot(withDecorView(hasDescendant(withId(id))))
-                .perform(click());
-        onView(isRoot()).perform(SLEEP);
-    }
-
-    public static void sleepForFloatingToolbarPopup() {
-        onView(isRoot()).perform(SLEEP);
-    }
-
-    /**
-     * Asserts that the floating toolbar contains the specified item.
-     *
-     * @param itemLabel label of the item.
-     * @throws AssertionError if the assertion fails
-     */
-    public static void assertFloatingToolbarContainsItem(String itemLabel) {
-        try{
-            onFloatingToolBar().check(matches(hasDescendant(withText(itemLabel))));
-        } catch (AssertionError e) {
-            try{
-                toggleOverflow();
-            } catch (NoMatchingViewException | NoMatchingRootException e2) {
-                // No overflow items.
-                throw e;
-            }
-            try{
-                onFloatingToolBar().check(matches(hasDescendant(withText(itemLabel))));
-            } finally {
-                toggleOverflow();
-            }
-        }
-    }
-
-    /**
-     * Asserts that the floating toolbar contains a specified item at a specified index.
-     *
-     * @param menuItemId id of the menu item
-     * @param index expected index of the menu item in the floating toolbar
-     * @throws AssertionError if the assertion fails
-     */
-    public static void assertFloatingToolbarItemIndex(final int menuItemId, final int index) {
-        onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
-            private List<Integer> menuItemIds = new ArrayList<>();
-
-            @Override
-            public boolean matchesSafely(View view) {
-                collectMenuItemIds(view);
-                return menuItemIds.size() > index && menuItemIds.get(index) == menuItemId;
-            }
-
-            @Override
-            public void describeTo(Description description) {}
-
-            private void collectMenuItemIds(View view) {
-                if (view.getTag() instanceof MenuItemRepr) {
-                    menuItemIds.add(((MenuItemRepr) view.getTag()).itemId);
-                } else if (view instanceof ViewGroup) {
-                    ViewGroup viewGroup = (ViewGroup) view;
-                    for (int i = 0; i < viewGroup.getChildCount(); i++) {
-                        collectMenuItemIds(viewGroup.getChildAt(i));
-                    }
-                }
-            }
-        }));
-    }
-
-    /**
-     * Asserts that the floating toolbar doesn't contain the specified item.
-     *
-     * @param itemLabel label of the item.
-     * @throws AssertionError if the assertion fails
-     */
-    public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
-        final Predicate<View> hasMenuItemLabel = view ->
-                view.getTag() instanceof MenuItemRepr
-                        && itemLabel.equals(((MenuItemRepr) view.getTag()).title);
-        assertFloatingToolbarMenuItem(hasMenuItemLabel, false);
-    }
-
-    /**
-     * Asserts that the floating toolbar does not contain a menu item with the specified id.
-     *
-     * @param menuItemId id of the menu item
-     * @throws AssertionError if the assertion fails
-     */
-    public static void assertFloatingToolbarDoesNotContainItem(final int menuItemId) {
-        final Predicate<View> hasMenuItemId = view ->
-                view.getTag() instanceof MenuItemRepr
-                        && ((MenuItemRepr) view.getTag()).itemId == menuItemId;
-        assertFloatingToolbarMenuItem(hasMenuItemId, false);
-    }
-
-    private static void assertFloatingToolbarMenuItem(
-            final Predicate<View> predicate, final boolean positiveAssertion) {
-        onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
-            @Override
-            public boolean matchesSafely(View view) {
-                return positiveAssertion == containsItem(view);
-            }
-
-            @Override
-            public void describeTo(Description description) {}
-
-            private boolean containsItem(View view) {
-                if (predicate.test(view)) {
-                    return true;
-                } else if (view instanceof ViewGroup) {
-                    ViewGroup viewGroup = (ViewGroup) view;
-                    for (int i = 0; i < viewGroup.getChildCount(); i++) {
-                        if (containsItem(viewGroup.getChildAt(i))) {
-                            return true;
-                        }
-                    }
-                }
-                return false;
-            }
-        }));
-    }
-
-    /**
-     * Click specified item on the floating tool bar.
-     *
-     * @param itemLabel label of the item.
-     */
-    public static void clickFloatingToolbarItem(String itemLabel) {
-        try{
-            onFloatingToolBarItem(withText(itemLabel)).check(matches(isDisplayed()));
-        } catch (AssertionError e) {
-            // Try to find the item in the overflow menu.
-            toggleOverflow();
-        }
-        onFloatingToolBarItem(withText(itemLabel)).perform(click());
-    }
-
-    /**
-     * ViewAction to sleep to wait floating toolbar's animation.
-     */
-    private static final ViewAction SLEEP = new ViewAction() {
-        private static final long SLEEP_DURATION = 400;
-
-        @Override
-        public Matcher<View> getConstraints() {
-            return isDisplayed();
-        }
-
-        @Override
-        public String getDescription() {
-            return "Sleep " + SLEEP_DURATION + " ms.";
-        }
-
-        @Override
-        public void perform(UiController uiController, View view) {
-            uiController.loopMainThreadForAtLeast(SLEEP_DURATION);
-        }
-    };
-}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 52cb9f3..a52d2e8 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -86,11 +86,13 @@
         mController.attachToDisplayArea(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
                 null /* options */);
 
-        assertThat(mController.mAttachedToDisplayArea).isTrue();
+        assertThat(mController.mAttachedToDisplayArea).isEqualTo(
+                WindowContextController.AttachStatus.STATUS_ATTACHED);
 
         mController.detachIfNeeded();
 
-        assertThat(mController.mAttachedToDisplayArea).isFalse();
+        assertThat(mController.mAttachedToDisplayArea).isEqualTo(
+                WindowContextController.AttachStatus.STATUS_DETACHED);
     }
 
     @Test(expected = IllegalStateException.class)
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 cd42a34..23ec3ea 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -2020,6 +2020,7 @@
                 .check(matches(isDisplayed()));
     }
 
+    @Ignore // b/220067877
     @Test
     public void testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown() {
         // enable the work tab feature flag
@@ -2304,6 +2305,7 @@
         assertThat(logger.numCalls(), is(5));
     }
 
+    @Ignore // b/220067877
     @Test
     public void testCopyTextToClipboardLogging() throws Exception {
         Intent sendIntent = createSendTextIntent();
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index ac5daf0..21b2cb0 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -481,6 +481,9 @@
         <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
         <permission name="android.permission.NEARBY_WIFI_DEVICES" />
         <permission name="android.permission.OVERRIDE_WIFI_CONFIG" />
+        <!-- Permission needed for CTS test - ConcurrencyTest#testP2pExternalApprover
+             P2P external approver API sets require MANAGE_WIFI_AUTO_JOIN permission. -->
+        <permission name="android.permission.MANAGE_WIFI_AUTO_JOIN" />
         <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
         <permission name="android.permission.BIND_CARRIER_SERVICES"/>
         <!-- Permission required for CTS test - MusicRecognitionManagerTest -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index e50ad38..9b41468 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -52,9 +52,6 @@
  */
 public class BackAnimationController implements RemoteCallable<BackAnimationController> {
 
-    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
-    public static final boolean IS_ENABLED = SystemProperties
-            .getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
     private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP =
             "persist.debug.back_predictability_progress_threshold";
     private static final int PROGRESS_THRESHOLD = SystemProperties
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 9219baa..c6a68dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1237,7 +1237,7 @@
                 b.getExpandedView().updateFontSize();
             }
         }
-        if (mBubbleOverflow != null) {
+        if (mBubbleOverflow != null && mBubbleOverflow.getExpandedView() != null) {
             mBubbleOverflow.getExpandedView().updateFontSize();
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 656dae3..79a24b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -40,6 +40,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
 
 import java.lang.ref.WeakReference;
@@ -105,9 +106,8 @@
 
     private CompatUICallback mCallback;
 
-    // Only show once automatically in the process life.
-    private boolean mHasShownSizeCompatHint;
-    private boolean mHasShownCameraCompatHint;
+    // Only show each hint once automatically in the process life.
+    private final CompatUIHintsState mCompatUIHintsState;
 
     // Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
     // be shown.
@@ -127,6 +127,7 @@
         mMainExecutor = mainExecutor;
         mDisplayController.addDisplayWindowListener(this);
         mImeController.addPositionProcessor(this);
+        mCompatUIHintsState = new CompatUIHintsState();
     }
 
     /** Returns implementation of {@link CompatUI}. */
@@ -259,19 +260,9 @@
     @VisibleForTesting
     CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
             ShellTaskOrganizer.TaskListener taskListener) {
-        final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
+        return new CompatUIWindowManager(context,
                 taskInfo, mSyncQueue, mCallback, taskListener,
-                mDisplayController.getDisplayLayout(taskInfo.displayId), mHasShownSizeCompatHint,
-                mHasShownCameraCompatHint);
-        // TODO(b/218304113): updates values only if hints are actually shown to the user.
-        // Only show hints for the first time.
-        if (taskInfo.topActivityInSizeCompat) {
-            mHasShownSizeCompatHint = true;
-        }
-        if (taskInfo.hasCameraCompatControl()) {
-            mHasShownCameraCompatHint = true;
-        }
-        return compatUIWindowManager;
+                mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState);
     }
 
     private void createOrUpdateLetterboxEduLayout(TaskInfo taskInfo,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 7c6780a..bce3ec4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -59,9 +59,7 @@
     int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
 
     @VisibleForTesting
-    boolean mShouldShowSizeCompatHint;
-    @VisibleForTesting
-    boolean mShouldShowCameraCompatHint;
+    CompatUIHintsState mCompatUIHintsState;
 
     @Nullable
     @VisibleForTesting
@@ -70,13 +68,12 @@
     CompatUIWindowManager(Context context, TaskInfo taskInfo,
             SyncTransactionQueue syncQueue, CompatUICallback callback,
             ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
-            boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
+            CompatUIHintsState compatUIHintsState) {
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         mCallback = callback;
         mHasSizeCompat = taskInfo.topActivityInSizeCompat;
         mCameraCompatControlState = taskInfo.cameraCompatControlState;
-        mShouldShowSizeCompatHint = !hasShownSizeCompatHint;
-        mShouldShowCameraCompatHint = !hasShownCameraCompatHint;
+        mCompatUIHintsState = compatUIHintsState;
     }
 
     @Override
@@ -212,18 +209,18 @@
         }
         // Size Compat mode restart button.
         mLayout.setRestartButtonVisibility(mHasSizeCompat);
-        if (mHasSizeCompat && mShouldShowSizeCompatHint) {
+        // Only show by default for the first time.
+        if (mHasSizeCompat && !mCompatUIHintsState.mHasShownSizeCompatHint) {
             mLayout.setSizeCompatHintVisibility(/* show= */ true);
-            // Only show by default for the first time.
-            mShouldShowSizeCompatHint = false;
+            mCompatUIHintsState.mHasShownSizeCompatHint = true;
         }
 
         // Camera control for stretched issues.
         mLayout.setCameraControlVisibility(shouldShowCameraControl());
-        if (shouldShowCameraControl() && mShouldShowCameraCompatHint) {
+        // Only show by default for the first time.
+        if (shouldShowCameraControl() && !mCompatUIHintsState.mHasShownCameraCompatHint) {
             mLayout.setCameraCompatHintVisibility(/* show= */ true);
-            // Only show by default for the first time.
-            mShouldShowCameraCompatHint = false;
+            mCompatUIHintsState.mHasShownCameraCompatHint = true;
         }
         if (shouldShowCameraControl()) {
             mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
@@ -234,4 +231,15 @@
         return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
                 && mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
     }
+
+    /**
+     * A class holding the state of the compat UI hints, which is shared between all compat UI
+     * window managers.
+     */
+    static class CompatUIHintsState {
+        @VisibleForTesting
+        boolean mHasShownSizeCompatHint;
+        @VisibleForTesting
+        boolean mHasShownCameraCompatHint;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 2e54c79..c94f3d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -168,8 +168,8 @@
 
     @WMSingleton
     @Provides
-    static DragAndDrop provideDragAndDrop(DragAndDropController dragAndDropController) {
-        return dragAndDropController.asDragAndDrop();
+    static Optional<DragAndDrop> provideDragAndDrop(DragAndDropController dragAndDropController) {
+        return Optional.of(dragAndDropController.asDragAndDrop());
     }
 
     @WMSingleton
@@ -184,8 +184,8 @@
 
     @WMSingleton
     @Provides
-    static CompatUI provideCompatUI(CompatUIController compatUIController) {
-        return compatUIController.asCompatUI();
+    static Optional<CompatUI> provideCompatUI(CompatUIController compatUIController) {
+        return Optional.of(compatUIController.asCompatUI());
     }
 
     @WMSingleton
@@ -699,10 +699,7 @@
             Context context,
             @ShellMainThread ShellExecutor shellExecutor
     ) {
-        if (BackAnimationController.IS_ENABLED) {
-            return Optional.of(
-                    new BackAnimationController(shellExecutor, context));
-        }
-        return Optional.empty();
+        return Optional.of(
+                new BackAnimationController(shellExecutor, context));
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
index 5c205f9..8f9636c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
@@ -27,7 +27,10 @@
 import android.os.Looper;
 import android.os.Trace;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.wm.shell.R;
 import com.android.wm.shell.common.HandlerExecutor;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
@@ -35,7 +38,6 @@
 import com.android.wm.shell.common.annotations.ShellAnimationThread;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
-import com.android.wm.shell.R;
 
 import dagger.Module;
 import dagger.Provides;
@@ -53,7 +55,7 @@
     /**
      * Returns whether to enable a separate shell thread for the shell features.
      */
-    private static boolean enableShellMainThread(Context context) {
+    public static boolean enableShellMainThread(Context context) {
         return context.getResources().getBoolean(R.bool.config_enableShellMainThread);
     }
 
@@ -85,23 +87,41 @@
     }
 
     /**
+     * Creates a shell main thread to be injected into the shell components.  This does not provide
+     * the {@param HandleThread}, but is used to create the thread prior to initializing the
+     * WM component, and is explicitly bound.
+     *
+     * See {@link com.android.systemui.SystemUIFactory#init(Context, boolean)}.
+     */
+    public static HandlerThread createShellMainThread() {
+        HandlerThread mainThread = new HandlerThread("wmshell.main", THREAD_PRIORITY_DISPLAY);
+        return mainThread;
+    }
+
+    /**
      * Shell main-thread Handler, don't use this unless really necessary (ie. need to dedupe
      * multiple types of messages, etc.)
+     *
+     * @param mainThread If non-null, this thread is expected to be started already
      */
     @WMSingleton
     @Provides
     @ShellMainThread
     public static Handler provideShellMainHandler(Context context,
+            @Nullable @ShellMainThread HandlerThread mainThread,
             @ExternalMainThread Handler sysuiMainHandler) {
         if (enableShellMainThread(context)) {
-             HandlerThread mainThread = new HandlerThread("wmshell.main", THREAD_PRIORITY_DISPLAY);
-             mainThread.start();
-             if (Build.IS_DEBUGGABLE) {
-                 mainThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
-                 mainThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
-                         MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
-             }
-             return Handler.createAsync(mainThread.getLooper());
+            if (mainThread == null) {
+                // If this thread wasn't pre-emptively started, then create and start it
+                mainThread = createShellMainThread();
+                mainThread.start();
+            }
+            if (Build.IS_DEBUGGABLE) {
+                mainThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
+                mainThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
+                        MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
+            }
+            return Handler.createAsync(mainThread.getLooper());
         }
         return sysuiMainHandler;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 67b3983..1eb9501 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -310,6 +310,10 @@
         return mPipTransitionState.isInPip();
     }
 
+    private boolean isLaunchIntoPipTask() {
+        return mPictureInPictureParams != null && mPictureInPictureParams.isLaunchIntoPip();
+    }
+
     /**
      * Returns whether the entry animation is waiting to be started.
      */
@@ -397,6 +401,10 @@
         }
 
         final WindowContainerTransaction wct = new WindowContainerTransaction();
+        if (isLaunchIntoPipTask()) {
+            exitLaunchIntoPipTask(wct);
+            return;
+        }
 
         if (ENABLE_SHELL_TRANSITIONS) {
             if (requestEnterSplit && mSplitScreenOptional.isPresent()) {
@@ -468,6 +476,14 @@
         });
     }
 
+    private void exitLaunchIntoPipTask(WindowContainerTransaction wct) {
+        wct.startTask(mTaskInfo.launchIntoPipHostTaskId, null /* ActivityOptions */);
+        mTaskOrganizer.applyTransaction(wct);
+
+        // Remove the PiP with fade-out animation right after the host Task is brought to front.
+        removePip();
+    }
+
     private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
         // Reset the final windowing mode.
         wct.setWindowingMode(mToken, getOutPipWindowingMode());
@@ -563,6 +579,13 @@
             Log.d(TAG, "Alpha animation is expired. Use bounds animation.");
             mOneShotAnimationType = ANIM_TYPE_BOUNDS;
         }
+
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            // For Shell transition, we will animate the window in PipTransition#startAnimation
+            // instead of #onTaskAppeared.
+            return;
+        }
+
         if (mWaitForFixedRotation) {
             onTaskAppearedWithFixedRotation();
             return;
@@ -572,15 +595,6 @@
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
 
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
-                mPipMenuController.attach(mLeash);
-            } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
-                mOneShotAnimationType = ANIM_TYPE_BOUNDS;
-            }
-            return;
-        }
-
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
             mPipMenuController.attach(mLeash);
             final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
@@ -729,7 +743,7 @@
     }
 
     /**
-     * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
+     * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int, boolean)}.
      * Meanwhile this callback is invoked whenever the task is removed. For instance:
      *   - as a result of removeRootTasksInWindowingModes from WM
      *   - activity itself is died
@@ -813,6 +827,16 @@
         mNextRotation = newRotation;
         mWaitForFixedRotation = true;
 
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            // The fixed rotation will also be included in the transition info. However, if it is
+            // not a PIP transition (such as open another app to different orientation),
+            // PIP transition handler may not be aware of the fixed rotation start.
+            // Notify the PIP transition handler so that it can fade out the PIP window early for
+            // fixed transition of other windows.
+            mPipTransitionController.onFixedRotationStarted();
+            return;
+        }
+
         if (mPipTransitionState.isInPip()) {
             // Fade out the existing PiP to avoid jump cut during seamless rotation.
             fadeExistingPip(false /* show */);
@@ -824,6 +848,10 @@
         if (!mWaitForFixedRotation) {
             return;
         }
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            clearWaitForFixedRotation();
+            return;
+        }
         if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) {
             if (mPipTransitionState.getInSwipePipToHomeTransition()) {
                 onEndOfSwipePipToHomeTransition();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 3e5d5f6..60aac68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.pip;
 
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.util.RotationUtils.deltaRotation;
@@ -32,9 +33,11 @@
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
 import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+import static com.android.wm.shell.pip.PipTransitionState.ENTERED_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
@@ -47,6 +50,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
@@ -86,6 +90,16 @@
     /** The Task window that is currently in PIP windowing mode. */
     @Nullable
     private WindowContainerToken mCurrentPipTaskToken;
+    /** Whether display is in fixed rotation. */
+    private boolean mInFixedRotation;
+    /**
+     * The rotation that the display will apply after expanding PiP to fullscreen. This is only
+     * meaningful if {@link #mInFixedRotation} is true.
+     */
+    @Surface.Rotation
+    private int mFixedRotation;
+    /** Whether the PIP window has fade out for fixed rotation. */
+    private boolean mHasFadeOut;
 
     public PipTransition(Context context,
             PipBoundsState pipBoundsState,
@@ -136,35 +150,41 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
+        final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
+        mInFixedRotation = fixedRotationChange != null;
+        mFixedRotation = mInFixedRotation
+                ? fixedRotationChange.getEndFixedRotation()
+                : ROTATION_UNDEFINED;
+
         // Exiting PIP.
         final int type = info.getType();
         if (transition.equals(mExitTransition)) {
             mExitDestinationBounds.setEmpty();
             mExitTransition = null;
-
+            mHasFadeOut = false;
             if (mFinishCallback != null) {
                 mFinishCallback.onTransitionFinished(null, null);
                 mFinishCallback = null;
                 throw new RuntimeException("Previous callback not called, aborting exit PIP.");
             }
 
-            final TransitionInfo.Change exitPipChange = findCurrentPipChange(info);
-            if (exitPipChange == null) {
+            if (currentPipChange == null) {
                 throw new RuntimeException("Cannot find the pip window for exit-pip transition.");
             }
 
             switch (type) {
                 case TRANSIT_EXIT_PIP:
                     startExitAnimation(info, startTransaction, finishTransaction, finishCallback,
-                            exitPipChange);
+                            currentPipChange);
                     break;
                 case TRANSIT_EXIT_PIP_TO_SPLIT:
                     startExitToSplitAnimation(info, startTransaction, finishTransaction,
-                            finishCallback, exitPipChange);
+                            finishCallback, currentPipChange);
                     break;
                 case TRANSIT_REMOVE_PIP:
                     removePipImmediately(info, startTransaction, finishTransaction, finishCallback,
-                            exitPipChange);
+                            currentPipChange);
                     break;
                 default:
                     throw new IllegalStateException("mExitTransition with unexpected transit type="
@@ -177,7 +197,6 @@
         // The previous PIP Task is no longer in PIP, but this is not an exit transition (This can
         // happen when a new activity requests enter PIP). In this case, we just show this Task in
         // its end state, and play other animation as normal.
-        final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
         if (currentPipChange != null
                 && currentPipChange.getTaskInfo().getWindowingMode() != WINDOWING_MODE_PINNED) {
             resetPrevPip(currentPipChange, startTransaction);
@@ -193,6 +212,12 @@
         if (currentPipChange != null) {
             updatePipForUnhandledTransition(currentPipChange, startTransaction, finishTransaction);
         }
+
+        // Fade in the fadeout PIP when the fixed rotation is finished.
+        if (mPipTransitionState.isInPip() && !mInFixedRotation && mHasFadeOut) {
+            fadeExistingPip(true /* show */);
+        }
+
         return false;
     }
 
@@ -242,9 +267,8 @@
     public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
             @PipAnimationController.TransitionDirection int direction,
             @Nullable SurfaceControl.Transaction tx) {
-
         if (isInPipDirection(direction)) {
-            mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
+            mPipTransitionState.setTransitionState(ENTERED_PIP);
         }
         // If there is an expected exit transition, then the exit will be "merged" into this
         // transition so don't fire the finish-callback in that case.
@@ -268,6 +292,16 @@
         mFinishCallback = null;
     }
 
+    @Override
+    public void onFixedRotationStarted() {
+        // The transition with this fixed rotation may be handled by other handler before reaching
+        // PipTransition, so we cannot do this in #startAnimation.
+        if (mPipTransitionState.getTransitionState() == ENTERED_PIP && !mHasFadeOut) {
+            // Fade out the existing PiP to avoid jump cut during seamless rotation.
+            fadeExistingPip(false /* show */);
+        }
+    }
+
     @Nullable
     private TransitionInfo.Change findCurrentPipChange(@NonNull TransitionInfo info) {
         if (mCurrentPipTaskToken == null) {
@@ -282,6 +316,17 @@
         return null;
     }
 
+    @Nullable
+    private TransitionInfo.Change findFixedRotationChange(@NonNull TransitionInfo info) {
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change.getEndFixedRotation() != ROTATION_UNDEFINED) {
+                return change;
+            }
+        }
+        return null;
+    }
+
     private void startExitAnimation(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
@@ -453,6 +498,7 @@
         }
         // Keep track of the PIP task.
         mCurrentPipTaskToken = enterPip.getContainer();
+        mHasFadeOut = false;
 
         if (mFinishCallback != null) {
             mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
@@ -465,12 +511,25 @@
             startTransaction.show(wallpaper.getLeash());
             startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
         }
+        // Make sure other open changes are visible as entering PIP. Some may be hidden in
+        // Transitions#setupStartState because the transition type is OPEN (such as auto-enter).
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change == enterPip || change == wallpaper) {
+                continue;
+            }
+            if (isOpeningType(change.getMode())) {
+                final SurfaceControl leash = change.getLeash();
+                startTransaction.show(leash).setAlpha(leash, 1.f);
+            }
+        }
 
         mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
         mFinishCallback = finishCallback;
+        final int endRotation = mInFixedRotation ? mFixedRotation : enterPip.getEndRotation();
         return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
                 startTransaction, finishTransaction, enterPip.getStartRotation(),
-                enterPip.getEndRotation());
+                endRotation);
     }
 
     private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
@@ -481,25 +540,36 @@
                 taskInfo.topActivityInfo);
         final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
         final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
+        int rotationDelta = deltaRotation(startRotation, endRotation);
+        Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+                taskInfo.pictureInPictureParams, currentBounds);
+        if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
+            // Need to get the bounds of new rotation in old rotation for fixed rotation,
+            sourceHintRect = computeRotatedBounds(rotationDelta, startRotation, endRotation,
+                    taskInfo, TRANSITION_DIRECTION_TO_PIP, destinationBounds, sourceHintRect);
+        }
         PipAnimationController.PipTransitionAnimator animator;
         // Set corner radius for entering pip.
         mSurfaceTransactionHelper
                 .crop(finishTransaction, leash, destinationBounds)
                 .round(finishTransaction, leash, true /* applyCornerRadius */);
+        mPipMenuController.attach(leash);
+
         if (taskInfo.pictureInPictureParams != null
                 && taskInfo.pictureInPictureParams.isAutoEnterEnabled()
                 && mPipTransitionState.getInSwipePipToHomeTransition()) {
             mOneShotAnimationType = ANIM_TYPE_BOUNDS;
-
-            // PiP menu is attached late in the process here to avoid any artifacts on the leash
-            // caused by addShellRoot when in gesture navigation mode.
-            mPipMenuController.attach(leash);
             SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
             tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9])
                     .setPosition(leash, destinationBounds.left, destinationBounds.top)
                     .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height());
             startTransaction.merge(tx);
             startTransaction.apply();
+            if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
+                // For fixed rotation, set the destination bounds to the new rotation coordinates
+                // at the end.
+                destinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
+            }
             mPipBoundsState.setBounds(destinationBounds);
             onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, null /* tx */);
             sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
@@ -507,17 +577,14 @@
             return true;
         }
 
-        int rotationDelta = deltaRotation(endRotation, startRotation);
         if (rotationDelta != Surface.ROTATION_0) {
             Matrix tmpTransform = new Matrix();
-            tmpTransform.postRotate(rotationDelta == Surface.ROTATION_90
-                    ? Surface.ROTATION_270 : Surface.ROTATION_90);
+            tmpTransform.postRotate(rotationDelta);
             startTransaction.setMatrix(leash, tmpTransform, new float[9]);
         }
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
-            final Rect sourceHintRect =
-                    PipBoundsAlgorithm.getValidSourceHintRect(
-                            taskInfo.pictureInPictureParams, currentBounds);
+            // Reverse the rotation for Shell transition animation.
+            rotationDelta = deltaRotation(rotationDelta, 0);
             animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
                     currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
                     0 /* startingAngle */, rotationDelta);
@@ -528,9 +595,6 @@
             }
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
             startTransaction.setAlpha(leash, 0f);
-            // PiP menu is attached late in the process here to avoid any artifacts on the leash
-            // caused by addShellRoot when in gesture navigation mode.
-            mPipMenuController.attach(leash);
             animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
                     0f, 1f);
             mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -541,12 +605,47 @@
         startTransaction.apply();
         animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
                 .setPipAnimationCallback(mPipAnimationCallback)
-                .setDuration(mEnterExitAnimationDuration)
-                .start();
+                .setDuration(mEnterExitAnimationDuration);
+        if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
+            // For fixed rotation, the animation destination bounds is in old rotation coordinates.
+            // Set the destination bounds to new coordinates after the animation is finished.
+            // ComputeRotatedBounds has changed the DisplayLayout without affecting the animation.
+            animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
+        }
+        animator.start();
 
         return true;
     }
 
+    /** Computes destination bounds in old rotation and returns source hint rect if available. */
+    @Nullable
+    private Rect computeRotatedBounds(int rotationDelta, int startRotation, int endRotation,
+            TaskInfo taskInfo, int direction, Rect outDestinationBounds,
+            @Nullable Rect sourceHintRect) {
+        if (direction == TRANSITION_DIRECTION_TO_PIP) {
+            mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation);
+            final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+            outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
+            // Transform the destination bounds to current display coordinates.
+            rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation);
+            // When entering PiP (from button navigation mode), adjust the source rect hint by
+            // display cutout if applicable.
+            if (sourceHintRect != null && taskInfo.displayCutoutInsets != null) {
+                if (rotationDelta == Surface.ROTATION_270) {
+                    sourceHintRect.offset(taskInfo.displayCutoutInsets.left,
+                            taskInfo.displayCutoutInsets.top);
+                }
+            }
+        } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
+            final Rect rotatedDestinationBounds = new Rect(outDestinationBounds);
+            rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(),
+                    rotationDelta);
+            return PipBoundsAlgorithm.getValidSourceHintRect(taskInfo.pictureInPictureParams,
+                    rotatedDestinationBounds);
+        }
+        return sourceHintRect;
+    }
+
     private void startExitToSplitAnimation(TransitionInfo info,
             SurfaceControl.Transaction startTransaction,
             SurfaceControl.Transaction finishTransaction,
@@ -595,6 +694,13 @@
         startTransaction.setCornerRadius(leash, 0);
         startTransaction.setPosition(leash, bounds.left, bounds.top);
 
+        if (mHasFadeOut && prevPipChange.getTaskInfo().isVisible()) {
+            if (mPipAnimationController.getCurrentAnimator() != null) {
+                mPipAnimationController.getCurrentAnimator().cancel();
+            }
+            startTransaction.setAlpha(leash, 1);
+        }
+        mHasFadeOut = false;
         mCurrentPipTaskToken = null;
         mPipOrganizer.onExitPipFinished(prevPipChange.getTaskInfo());
     }
@@ -615,6 +721,25 @@
                 .round(finishTransaction, leash, isInPip);
     }
 
+    /** Hides and shows the existing PIP during fixed rotation transition of other activities. */
+    private void fadeExistingPip(boolean show) {
+        final SurfaceControl leash = mPipOrganizer.getSurfaceControl();
+        final TaskInfo taskInfo = mPipOrganizer.getTaskInfo();
+        if (leash == null || !leash.isValid() || taskInfo == null) {
+            Log.w(TAG, "Invalid leash on fadeExistingPip: " + leash);
+            return;
+        }
+        final float alphaStart = show ? 0 : 1;
+        final float alphaEnd = show ? 1 : 0;
+        mPipAnimationController
+                .getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), alphaStart, alphaEnd)
+                .setTransitionDirection(TRANSITION_DIRECTION_SAME)
+                .setPipAnimationCallback(mPipAnimationCallback)
+                .setDuration(mEnterExitAnimationDuration)
+                .start();
+        mHasFadeOut = !show;
+    }
+
     private void finishResizeForMenu(Rect destinationBounds) {
         mPipMenuController.movePipMenu(null, null, destinationBounds);
         mPipMenuController.updateMenuBounds(destinationBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 3403fb5..02e713d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -123,6 +123,10 @@
     public void forceFinishTransition() {
     }
 
+    /** Called when the fixed rotation started. */
+    public void onFixedRotationStarted() {
+    }
+
     public PipTransitionController(PipBoundsState pipBoundsState,
             PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
             PipAnimationController pipAnimationController, Transitions transitions,
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 556742e..574a9f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -26,16 +26,8 @@
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6000s" />
         <option name="hidden-api-checks" value="false" />
-        <option name="device-listeners"
-                value="com.android.server.wm.flicker.TraceFileReadyListener" />
     </test>
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
-        <option name="pull-pattern-keys" value="(\w)+\.winscope" />
-        <option name="pull-pattern-keys" value="(\w)+\.mp4" />
-        <option name="collect-on-run-ended-only" value="false" />
-        <option name="clean-up" value="true" />
-    </metrics_collector>
-    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="directory-keys" value="/sdcard/flicker" />
         <option name="collect-on-run-ended-only" value="true" />
         <option name="clean-up" value="true" />
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
index 0a3321e..a57d3e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.bubble
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 19e020a..5fc80db 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index 8729bb6..87e927f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -24,7 +24,10 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.traces.region.RegionSubject
+import org.junit.Assume
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,6 +61,11 @@
 open class MovePipDownShelfHeightChangeTest(
     testSpec: FlickerTestParameter
 ) : MovePipShelfHeightTransition(testSpec) {
+    @Before
+    open fun before() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+    }
+
     /**
      * Defines the transition used to run the test
      */
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest_ShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest_ShellTransit.kt
index 6c80daa..0ff260b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest_ShellTransit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest_ShellTransit.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -58,7 +58,7 @@
     testSpec: FlickerTestParameter
 ) : MovePipDownShelfHeightChangeTest(testSpec) {
     @Before
-    fun before() {
+    override fun before() {
         Assume.assumeTrue(isShellTransitionsEnabled)
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index 4bc8eb1..388b5e0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.RequiresDevice
 import android.view.Surface
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 352805b..7d3e718 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -43,6 +43,7 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -77,8 +78,7 @@
         mWindowManager = new CompatUIWindowManager(mContext,
                 createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
                 mSyncTransactionQueue, mCallback, mTaskListener,
-                new DisplayLayout(), /* hasShownSizeCompatHint= */ false,
-                /* hasShownCameraCompatHint= */ false);
+                new DisplayLayout(), new CompatUIHintsState());
 
         mLayout = (CompatUILayout)
                 LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index f9cfd12..e79b803 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -51,6 +51,7 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -85,8 +86,7 @@
         mWindowManager = new CompatUIWindowManager(mContext,
                 createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
                 mSyncTransactionQueue, mCallback, mTaskListener,
-                new DisplayLayout(), /* hasShownSizeCompatHint= */ false,
-                /* hasShownCameraCompatHint= */ false);
+                new DisplayLayout(), new CompatUIHintsState());
 
         spyOn(mWindowManager);
         doReturn(mLayout).when(mWindowManager).inflateLayout();
@@ -102,7 +102,7 @@
         verify(mWindowManager, never()).inflateLayout();
 
         // Doesn't create hint popup.
-        mWindowManager.mShouldShowSizeCompatHint = false;
+        mWindowManager.mCompatUIHintsState.mHasShownSizeCompatHint = true;
         assertTrue(mWindowManager.createLayout(/* canShow= */ true));
 
         verify(mWindowManager).inflateLayout();
@@ -113,14 +113,14 @@
         clearInvocations(mWindowManager);
         clearInvocations(mLayout);
         mWindowManager.release();
-        mWindowManager.mShouldShowSizeCompatHint = true;
+        mWindowManager.mCompatUIHintsState.mHasShownSizeCompatHint = false;
         assertTrue(mWindowManager.createLayout(/* canShow= */ true));
 
         verify(mWindowManager).inflateLayout();
         assertNotNull(mLayout);
         verify(mLayout).setRestartButtonVisibility(/* show= */ true);
         verify(mLayout).setSizeCompatHintVisibility(/* show= */ true);
-        assertFalse(mWindowManager.mShouldShowSizeCompatHint);
+        assertTrue(mWindowManager.mCompatUIHintsState.mHasShownSizeCompatHint);
 
         // Returns false and doesn't create layout if has Size Compat is false.
         clearInvocations(mWindowManager);
@@ -140,7 +140,7 @@
         verify(mWindowManager, never()).inflateLayout();
 
         // Doesn't create hint popup.
-        mWindowManager.mShouldShowCameraCompatHint = false;
+        mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint = true;
         assertTrue(mWindowManager.createLayout(/* canShow= */ true));
 
         verify(mWindowManager).inflateLayout();
@@ -151,14 +151,14 @@
         clearInvocations(mWindowManager);
         clearInvocations(mLayout);
         mWindowManager.release();
-        mWindowManager.mShouldShowCameraCompatHint = true;
+        mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint = false;
         assertTrue(mWindowManager.createLayout(/* canShow= */ true));
 
         verify(mWindowManager).inflateLayout();
         assertNotNull(mLayout);
         verify(mLayout).setCameraControlVisibility(/* show= */ true);
         verify(mLayout).setCameraCompatHintVisibility(/* show= */ true);
-        assertFalse(mWindowManager.mShouldShowCameraCompatHint);
+        assertTrue(mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint);
 
         // Returns false and doesn't create layout if Camera Compat state is hidden
         clearInvocations(mWindowManager);
@@ -411,7 +411,7 @@
     public void testOnRestartButtonLongClicked_showHint() {
        // Not create hint popup.
         mWindowManager.mHasSizeCompat = true;
-        mWindowManager.mShouldShowSizeCompatHint = false;
+        mWindowManager.mCompatUIHintsState.mHasShownSizeCompatHint = true;
         mWindowManager.createLayout(/* canShow= */ true);
 
         verify(mWindowManager).inflateLayout();
@@ -423,10 +423,10 @@
     }
 
     @Test
-    public void testOnCamerControlLongClicked_showHint() {
+    public void testOnCameraControlLongClicked_showHint() {
        // Not create hint popup.
         mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        mWindowManager.mShouldShowCameraCompatHint = false;
+        mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint = true;
         mWindowManager.createLayout(/* canShow= */ true);
 
         verify(mWindowManager).inflateLayout();
diff --git a/location/java/android/location/SatellitePvt.java b/location/java/android/location/SatellitePvt.java
index 29888e1..f140c68 100644
--- a/location/java/android/location/SatellitePvt.java
+++ b/location/java/android/location/SatellitePvt.java
@@ -144,8 +144,8 @@
     private final ClockInfo mClockInfo;
     private final double mIonoDelayMeters;
     private final double mTropoDelayMeters;
-    private final int mTimeOfClock;
-    private final int mTimeOfEphemeris;
+    private final long mTimeOfClock;
+    private final long mTimeOfEphemeris;
     private final int mIssueOfDataClock;
     private final int mIssueOfDataEphemeris;
     @EphemerisSource
@@ -457,8 +457,8 @@
             @Nullable ClockInfo clockInfo,
             double ionoDelayMeters,
             double tropoDelayMeters,
-            int timeOfClock,
-            int timeOfEphemeris,
+            long timeOfClock,
+            long timeOfEphemeris,
             int issueOfDataClock,
             int issueOfDataEphemeris,
             @EphemerisSource int ephemerisSource) {
@@ -547,26 +547,28 @@
     /**
      * Time of Clock.
      *
-     * <p>This is defined in GPS ICD200 documentation (e.g.,
-     * <a href="https://www.gps.gov/technical/icwg/IS-GPS-200H.pdf"></a>).
+     * <p>The value is in seconds since GPS epoch, regardless of the constellation.
+     *
+     * <p>The value is not encoded as in GPS ICD200 documentation.
      *
      * <p>This field is valid if {@link #hasTimeOfClock()} is true.
      */
-    @IntRange(from = 0, to = 604784)
-    public int getTimeOfClock() {
+    @IntRange(from = 0)
+    public long getTimeOfClock() {
         return mTimeOfClock;
     }
 
     /**
      * Time of ephemeris.
      *
-     * <p>This is defined in GPS ICD200 documentation (e.g.,
-     * <a href="https://www.gps.gov/technical/icwg/IS-GPS-200H.pdf"></a>).
+     * <p>The value is in seconds since GPS epoch, regardless of the constellation.
+     *
+     * <p>The value is not encoded as in GPS ICD200 documentation.
      *
      * <p>This field is valid if {@link #hasTimeOfEphemeris()} is true.
      */
-    @IntRange(from = 0, to = 604784)
-    public int getTimeOfEphemeris() {
+    @IntRange(from = 0)
+    public long getTimeOfEphemeris() {
         return mTimeOfEphemeris;
     }
 
@@ -630,8 +632,8 @@
                             android.location.SatellitePvt.ClockInfo.class);
                     double ionoDelayMeters = in.readDouble();
                     double tropoDelayMeters = in.readDouble();
-                    int toc = in.readInt();
-                    int toe = in.readInt();
+                    long toc = in.readLong();
+                    long toe = in.readLong();
                     int iodc = in.readInt();
                     int iode = in.readInt();
                     int ephemerisSource = in.readInt();
@@ -669,8 +671,8 @@
         parcel.writeParcelable(mClockInfo, flags);
         parcel.writeDouble(mIonoDelayMeters);
         parcel.writeDouble(mTropoDelayMeters);
-        parcel.writeInt(mTimeOfClock);
-        parcel.writeInt(mTimeOfEphemeris);
+        parcel.writeLong(mTimeOfClock);
+        parcel.writeLong(mTimeOfEphemeris);
         parcel.writeInt(mIssueOfDataClock);
         parcel.writeInt(mIssueOfDataEphemeris);
         parcel.writeInt(mEphemerisSource);
@@ -707,8 +709,8 @@
         @Nullable private ClockInfo mClockInfo;
         private double mIonoDelayMeters;
         private double mTropoDelayMeters;
-        private int mTimeOfClock;
-        private int mTimeOfEphemeris;
+        private long mTimeOfClock;
+        private long mTimeOfEphemeris;
         private int mIssueOfDataClock;
         private int mIssueOfDataEphemeris;
         @EphemerisSource
@@ -721,8 +723,7 @@
          * @return builder object
          */
         @NonNull
-        public Builder setPositionEcef(
-                @NonNull PositionEcef positionEcef) {
+        public Builder setPositionEcef(@NonNull PositionEcef positionEcef) {
             mPositionEcef = positionEcef;
             updateFlags();
             return this;
@@ -735,8 +736,7 @@
          * @return builder object
          */
         @NonNull
-        public Builder setVelocityEcef(
-                @NonNull VelocityEcef velocityEcef) {
+        public Builder setVelocityEcef(@NonNull VelocityEcef velocityEcef) {
             mVelocityEcef = velocityEcef;
             updateFlags();
             return this;
@@ -749,8 +749,7 @@
          * @return builder object
          */
         @NonNull
-        public Builder setClockInfo(
-                @NonNull ClockInfo clockInfo) {
+        public Builder setClockInfo(@NonNull ClockInfo clockInfo) {
             mClockInfo = clockInfo;
             updateFlags();
             return this;
@@ -793,12 +792,16 @@
         /**
          * Set time of clock in seconds.
          *
+         * <p>The value is in seconds since GPS epoch, regardless of the constellation.
+         *
+         * <p>The value is not encoded as in GPS ICD200 documentation.
+         *
          * @param timeOfClock time of clock (seconds)
          * @return builder object
          */
         @NonNull
-        public Builder setTimeOfClock(@IntRange(from = 0, to = 604784) int timeOfClock) {
-            Preconditions.checkArgumentInRange(timeOfClock, 0, 604784, "timeOfClock");
+        public Builder setTimeOfClock(@IntRange(from = 0) long timeOfClock) {
+            Preconditions.checkArgumentNonnegative(timeOfClock);
             mTimeOfClock = timeOfClock;
             mFlags = (byte) (mFlags | HAS_TIME_OF_CLOCK);
             return this;
@@ -807,12 +810,16 @@
         /**
          * Set time of ephemeris in seconds.
          *
+         * <p>The value is in seconds since GPS epoch, regardless of the constellation.
+         *
+         * <p>The value is not encoded as in GPS ICD200 documentation.
+         *
          * @param timeOfEphemeris time of ephemeris (seconds)
          * @return builder object
          */
         @NonNull
-        public Builder setTimeOfEphemeris(@IntRange(from = 0, to = 604784) int timeOfEphemeris) {
-            Preconditions.checkArgumentInRange(timeOfEphemeris, 0, 604784, "timeOfEphemeris");
+        public Builder setTimeOfEphemeris(@IntRange(from = 0) int timeOfEphemeris) {
+            Preconditions.checkArgumentNonnegative(timeOfEphemeris);
             mTimeOfEphemeris = timeOfEphemeris;
             mFlags = (byte) (mFlags | HAS_TIME_OF_EPHEMERIS);
             return this;
diff --git a/media/java/Android.bp b/media/java/Android.bp
index c7c1d54..6878f9d 100644
--- a/media/java/Android.bp
+++ b/media/java/Android.bp
@@ -16,7 +16,9 @@
     exclude_srcs: [
         ":framework-media-tv-tunerresourcemanager-sources-aidl",
     ],
-    visibility: ["//frameworks/base"],
+    visibility: [
+        "//frameworks/base",
+    ],
 }
 
 filegroup {
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index 5348d4e..74c5499 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -112,20 +112,11 @@
     // Binder stub for receiving device notifications from MidiService
     private class DeviceListener extends IMidiDeviceListener.Stub {
         private final DeviceCallback mCallback;
-        private final Handler mHandler;
         private final Executor mExecutor;
         private final int mTransport;
 
-        DeviceListener(DeviceCallback callback, Handler handler, int transport) {
-            mCallback = callback;
-            mHandler = handler;
-            mExecutor = null;
-            mTransport = transport;
-        }
-
         DeviceListener(DeviceCallback callback, Executor executor, int transport) {
             mCallback = callback;
-            mHandler = null;
             mExecutor = executor;
             mTransport = transport;
         }
@@ -136,13 +127,6 @@
                 if (mExecutor != null) {
                     mExecutor.execute(() ->
                             mCallback.onDeviceAdded(device));
-                } else if (mHandler != null) {
-                    final MidiDeviceInfo deviceF = device;
-                    mHandler.post(new Runnable() {
-                            @Override public void run() {
-                                mCallback.onDeviceAdded(deviceF);
-                            }
-                        });
                 } else {
                     mCallback.onDeviceAdded(device);
                 }
@@ -155,13 +139,6 @@
                 if (mExecutor != null) {
                     mExecutor.execute(() ->
                             mCallback.onDeviceRemoved(device));
-                } else if (mHandler != null) {
-                    final MidiDeviceInfo deviceF = device;
-                    mHandler.post(new Runnable() {
-                            @Override public void run() {
-                                mCallback.onDeviceRemoved(deviceF);
-                            }
-                        });
                 } else {
                     mCallback.onDeviceRemoved(device);
                 }
@@ -173,13 +150,6 @@
             if (mExecutor != null) {
                 mExecutor.execute(() ->
                         mCallback.onDeviceStatusChanged(status));
-            } else if (mHandler != null) {
-                final MidiDeviceStatus statusF = status;
-                mHandler.post(new Runnable() {
-                        @Override public void run() {
-                            mCallback.onDeviceStatusChanged(statusF);
-                        }
-                    });
             } else {
                 mCallback.onDeviceStatusChanged(status);
             }
@@ -275,7 +245,11 @@
      */
     @Deprecated
     public void registerDeviceCallback(DeviceCallback callback, Handler handler) {
-        DeviceListener deviceListener = new DeviceListener(callback, handler,
+        Executor executor = null;
+        if (handler != null) {
+            executor = handler::post;
+        }
+        DeviceListener deviceListener = new DeviceListener(callback, executor,
                 TRANSPORT_MIDI_BYTE_STREAM);
         try {
             mService.registerListener(mToken, deviceListener);
diff --git a/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp
index f8a8168..39cbaf7 100644
--- a/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp
@@ -102,15 +102,10 @@
     }
 }
 
-static int deleteTagData(JNIEnv* /* env */, jclass /* clazz */, jint uid) {
-    return qtaguid_deleteTagData(0, uid);
-}
-
 static const JNINativeMethod gMethods[] = {
         {"nativeGetTotalStat", "(I)J", (void*)getTotalStat},
         {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)getIfaceStat},
         {"nativeGetUidStat", "(II)J", (void*)getUidStat},
-        {"nativeDeleteTagData", "(I)I", (void*)deleteTagData},
 };
 
 int register_android_server_net_NetworkStatsService(JNIEnv* env) {
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index e8b3d4c..ef9ebb5 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -51,6 +51,7 @@
 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG;
 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
 import static android.os.Trace.TRACE_TAG_NETWORK;
+import static android.system.OsConstants.ENOENT;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
@@ -122,7 +123,6 @@
 import android.service.NetworkInterfaceProto;
 import android.service.NetworkStatsServiceDumpProto;
 import android.system.ErrnoException;
-import android.system.Os;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionPlan;
 import android.text.TextUtils;
@@ -221,6 +221,14 @@
     // This is current path but may be changed soon.
     private static final String UID_COUNTERSET_MAP_PATH =
             "/sys/fs/bpf/map_netd_uid_counterset_map";
+    private static final String COOKIE_TAG_MAP_PATH =
+            "/sys/fs/bpf/map_netd_cookie_tag_map";
+    private static final String APP_UID_STATS_MAP_PATH =
+            "/sys/fs/bpf/map_netd_app_uid_stats_map";
+    private static final String STATS_MAP_A_PATH =
+            "/sys/fs/bpf/map_netd_stats_map_A";
+    private static final String STATS_MAP_B_PATH =
+            "/sys/fs/bpf/map_netd_stats_map_B";
 
     private final Context mContext;
     private final NetworkStatsFactory mStatsFactory;
@@ -348,6 +356,10 @@
      */
     private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
     private final IBpfMap<U32, U8> mUidCounterSetMap;
+    private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap;
+    private final IBpfMap<StatsMapKey, StatsMapValue> mStatsMapA;
+    private final IBpfMap<StatsMapKey, StatsMapValue> mStatsMapB;
+    private final IBpfMap<UidStatsMapKey, StatsMapValue> mAppUidStatsMap;
 
     /** Data layer operation counters for splicing into other structures. */
     private NetworkStats mUidOperations = new NetworkStats(0L, 10);
@@ -481,6 +493,10 @@
         mInterfaceMapUpdater = mDeps.makeBpfInterfaceMapUpdater(mContext, mHandler);
         mInterfaceMapUpdater.start();
         mUidCounterSetMap = mDeps.getUidCounterSetMap();
+        mCookieTagMap = mDeps.getCookieTagMap();
+        mStatsMapA = mDeps.getStatsMapA();
+        mStatsMapB = mDeps.getStatsMapB();
+        mAppUidStatsMap = mDeps.getAppUidStatsMap();
     }
 
     /**
@@ -554,8 +570,48 @@
             }
         }
 
-        public TagStatsDeleter getTagStatsDeleter() {
-            return NetworkStatsService::nativeDeleteTagData;
+        /** Gets the cookie tag map */
+        public IBpfMap<CookieTagMapKey, CookieTagMapValue> getCookieTagMap() {
+            try {
+                return new BpfMap<CookieTagMapKey, CookieTagMapValue>(COOKIE_TAG_MAP_PATH,
+                        BpfMap.BPF_F_RDWR, CookieTagMapKey.class, CookieTagMapValue.class);
+            } catch (ErrnoException e) {
+                Log.wtf(TAG, "Cannot create cookie tag map: " + e);
+                return null;
+            }
+        }
+
+        /** Gets stats map A */
+        public IBpfMap<StatsMapKey, StatsMapValue> getStatsMapA() {
+            try {
+                return new BpfMap<StatsMapKey, StatsMapValue>(STATS_MAP_A_PATH,
+                        BpfMap.BPF_F_RDWR, StatsMapKey.class, StatsMapValue.class);
+            } catch (ErrnoException e) {
+                Log.wtf(TAG, "Cannot create stats map A: " + e);
+                return null;
+            }
+        }
+
+        /** Gets stats map B */
+        public IBpfMap<StatsMapKey, StatsMapValue> getStatsMapB() {
+            try {
+                return new BpfMap<StatsMapKey, StatsMapValue>(STATS_MAP_B_PATH,
+                        BpfMap.BPF_F_RDWR, StatsMapKey.class, StatsMapValue.class);
+            } catch (ErrnoException e) {
+                Log.wtf(TAG, "Cannot create stats map B: " + e);
+                return null;
+            }
+        }
+
+        /** Gets the uid stats map */
+        public IBpfMap<UidStatsMapKey, StatsMapValue> getAppUidStatsMap() {
+            try {
+                return new BpfMap<UidStatsMapKey, StatsMapValue>(APP_UID_STATS_MAP_PATH,
+                        BpfMap.BPF_F_RDWR, UidStatsMapKey.class, StatsMapValue.class);
+            } catch (ErrnoException e) {
+                Log.wtf(TAG, "Cannot create app uid stats map: " + e);
+                return null;
+            }
         }
     }
 
@@ -1802,6 +1858,63 @@
                 currentTime);
     }
 
+    // deleteKernelTagData can ignore ENOENT; otherwise we should log an error
+    private void logErrorIfNotErrNoent(final ErrnoException e, final String msg) {
+        if (e.errno != ENOENT) Log.e(TAG, msg, e);
+    }
+
+    private <K extends StatsMapKey, V extends StatsMapValue> void deleteStatsMapTagData(
+            IBpfMap<K, V> statsMap, int uid) {
+        try {
+            statsMap.forEach((key, value) -> {
+                if (key.uid == uid) {
+                    try {
+                        statsMap.deleteEntry(key);
+                    } catch (ErrnoException e) {
+                        logErrorIfNotErrNoent(e, "Failed to delete data(uid = " + key.uid + ")");
+                    }
+                }
+            });
+        } catch (ErrnoException e) {
+            Log.e(TAG, "FAILED to delete tag data from stats map", e);
+        }
+    }
+
+    /**
+     * Deletes uid tag data from CookieTagMap, StatsMapA, StatsMapB, and UidStatsMap
+     * @param uid
+     */
+    private void deleteKernelTagData(int uid) {
+        try {
+            mCookieTagMap.forEach((key, value) -> {
+                if (value.uid == uid) {
+                    try {
+                        mCookieTagMap.deleteEntry(key);
+                    } catch (ErrnoException e) {
+                        logErrorIfNotErrNoent(e, "Failed to delete data(cookie = " + key + ")");
+                    }
+                }
+            });
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Failed to delete tag data from cookie tag map", e);
+        }
+
+        deleteStatsMapTagData(mStatsMapA, uid);
+        deleteStatsMapTagData(mStatsMapB, uid);
+
+        try {
+            mUidCounterSetMap.deleteEntry(new U32(uid));
+        } catch (ErrnoException e) {
+            logErrorIfNotErrNoent(e, "Failed to delete tag data from uid counter set map");
+        }
+
+        try {
+            mAppUidStatsMap.deleteEntry(new UidStatsMapKey(uid));
+        } catch (ErrnoException e) {
+            logErrorIfNotErrNoent(e, "Failed to delete tag data from app uid stats map");
+        }
+    }
+
     /**
      * Clean up {@link #mUidRecorder} after UID is removed.
      */
@@ -1817,10 +1930,7 @@
 
         // Clear kernel stats associated with UID
         for (int uid : uids) {
-            final int ret = mDeps.getTagStatsDeleter().deleteTagData(uid);
-            if (ret < 0) {
-                Log.w(TAG, "problem clearing counters for uid " + uid + ": " + Os.strerror(-ret));
-            }
+            deleteKernelTagData(uid);
         }
     }
 
@@ -2402,12 +2512,4 @@
     private static native long nativeGetTotalStat(int type);
     private static native long nativeGetIfaceStat(String iface, int type);
     private static native long nativeGetUidStat(int uid, int type);
-
-    // TODO: use BpfNetMaps to delete tag data and remove this.
-    @VisibleForTesting
-    interface TagStatsDeleter {
-        int deleteTagData(int uid);
-    }
-
-    private static native int nativeDeleteTagData(int uid);
 }
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
deleted file mode 100644
index 139cd95..0000000
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
+++ /dev/null
@@ -1,44 +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.
-  -->
-
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:priv-android="http://schemas.android.com/apk/prv/res/android"
-    android:color="@color/ripple_color">
-
-    <item android:id="@android:id/background">
-        <layer-list android:paddingMode="stack">
-            <item
-                android:top="8dp"
-                android:bottom="8dp">
-
-                <shape>
-                    <corners android:radius="28dp"/>
-                    <solid android:color="?priv-android:attr/colorAccentPrimary"/>
-                    <size android:height="@dimen/spinner_height"/>
-                </shape>
-            </item>
-
-            <item
-                android:gravity="center|end"
-                android:width="18dp"
-                android:height="18dp"
-                android:end="8dp"
-                android:drawable="@drawable/arrow_drop_down"/>
-        </layer-list>
-    </item>
-</ripple>
diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_dropdown_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml
similarity index 78%
rename from packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_dropdown_view.xml
rename to packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml
index a342c84..cea1133 100644
--- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_dropdown_view.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2021 The Android Open Source Project
+     Copyright (C) 2022 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -18,8 +18,7 @@
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@android:id/text1"
-    style="@style/SettingsSpinnerTitleBar"
+    style="@style/SettingsSpinnerDropdown"
     android:gravity="center_vertical"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="@drawable/settings_spinner_dropdown_background"/>
+    android:layout_height="wrap_content"/>
diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
index 7d5b6db..4c75344 100644
--- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
@@ -22,7 +22,7 @@
     android:layout_marginStart="16dp"
     android:layout_marginEnd="16dp">
 
-    <com.android.settingslib.widget.settingsspinner.SettingsSpinner
+    <Spinner
         android:id="@+id/spinner"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/packages/SettingsLib/SettingsSpinner/res/values-night/colors.xml b/packages/SettingsLib/SettingsSpinner/res/values-v31/colors.xml
similarity index 72%
rename from packages/SettingsLib/SettingsSpinner/res/values-night/colors.xml
rename to packages/SettingsLib/SettingsSpinner/res/values-v31/colors.xml
index abcf822..8fda876 100644
--- a/packages/SettingsLib/SettingsSpinner/res/values-night/colors.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values-v31/colors.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
+<!-- Copyright (C) 2022 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -15,5 +15,6 @@
 -->
 
 <resources>
-    <color name="ripple_color">@*android:color/material_grey_900</color>
+    <color name="settingslib_spinner_title_color">@android:color/system_neutral1_900</color>
+    <color name="settingslib_spinner_dropdown_color">@android:color/system_neutral2_700</color>
 </resources>
diff --git a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml
new file mode 100644
index 0000000..fc3ec43
--- /dev/null
+++ b/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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>
+    <style name="SettingsSpinnerTitleBar">
+        <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
+        <item name="android:textColor">@color/settingslib_spinner_title_color</item>
+        <item name="android:maxLines">1</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:minHeight">@dimen/spinner_height</item>
+        <item name="android:paddingStart">16dp</item>
+        <item name="android:paddingEnd">36dp</item>
+        <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item>
+        <item name="android:paddingBottom">@dimen/spinner_padding_top_or_bottom</item>
+    </style>
+
+    <style name="SettingsSpinnerDropdown">
+        <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
+        <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item>
+        <item name="android:maxLines">1</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:minHeight">@dimen/spinner_height</item>
+        <item name="android:paddingStart">16dp</item>
+        <item name="android:paddingEnd">36dp</item>
+        <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item>
+        <item name="android:paddingBottom">@dimen/spinner_padding_top_or_bottom</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/SettingsSpinner/res/values/colors.xml b/packages/SettingsLib/SettingsSpinner/res/values/colors.xml
deleted file mode 100644
index 799b35e..0000000
--- a/packages/SettingsLib/SettingsSpinner/res/values/colors.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <color name="ripple_color">?android:attr/colorControlHighlight</color>
-</resources>
diff --git a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
index f665f38..8ea1f9a 100644
--- a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
@@ -18,10 +18,8 @@
 <resources>
     <style name="SettingsSpinnerTitleBar">
         <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
-        <item name="android:textColor">@android:color/black</item>
         <item name="android:maxLines">1</item>
         <item name="android:ellipsize">marquee</item>
-        <item name="android:minHeight">@dimen/spinner_height</item>
         <item name="android:paddingStart">16dp</item>
         <item name="android:paddingEnd">36dp</item>
         <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item>
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
similarity index 77%
rename from packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java
rename to packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
index 83da512..2611207 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.settingslib.widget.settingsspinner;
+package com.android.settingslib.widget;
 
 import android.content.Context;
+import android.os.Build;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 
-import com.android.settingslib.widget.R;
-
 /**
- * An ArrayAdapter which was used by {@link SettingsSpinner} with settings style.
+ * An ArrayAdapter which was used by Spinner with settings style.
+ * @param <T> the data type to be loaded.
  */
 public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> {
 
@@ -43,7 +43,7 @@
     public SettingsSpinnerAdapter(Context context) {
         super(context, DEFAULT_RESOURCE);
 
-        setDropDownViewResource(DFAULT_DROPDOWN_RESOURCE);
+        setDropDownViewResource(getDropdownResource());
         mDefaultInflater = LayoutInflater.from(context);
     }
 
@@ -59,6 +59,11 @@
      * drop down view.
      */
     public View getDefaultDropDownView(int position, View convertView, ViewGroup parent) {
-        return mDefaultInflater.inflate(DFAULT_DROPDOWN_RESOURCE, parent, false /* attachToRoot */);
+        return mDefaultInflater.inflate(getDropdownResource(), parent, false /* attachToRoot */);
+    }
+
+    private int getDropdownResource() {
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+                ? DFAULT_DROPDOWN_RESOURCE : android.R.layout.simple_spinner_dropdown_item;
     }
 }
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
index d993e44..6952875 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -20,16 +20,14 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.AdapterView;
+import android.widget.Spinner;
 
 import androidx.preference.Preference;
 import androidx.preference.Preference.OnPreferenceClickListener;
 import androidx.preference.PreferenceViewHolder;
 
-import com.android.settingslib.widget.settingsspinner.SettingsSpinner;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
-
 /**
- * This preference uses SettingsSpinner & SettingsSpinnerAdapter which provide default layouts for
+ * This preference uses Spinner & SettingsSpinnerAdapter which provide default layouts for
  * both view and drop down view of the Spinner.
  */
 public class SettingsSpinnerPreference extends Preference implements OnPreferenceClickListener {
@@ -113,7 +111,7 @@
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
-        final SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.spinner);
+        final Spinner spinner = (Spinner) holder.findViewById(R.id.spinner);
         spinner.setAdapter(mAdapter);
         spinner.setSelection(mPosition);
         spinner.setOnItemSelectedListener(mOnSelectedListener);
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
deleted file mode 100644
index 14286fa..0000000
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
+++ /dev/null
@@ -1,135 +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.settingslib.widget.settingsspinner;
-
-import android.content.Context;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.widget.Spinner;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.settingslib.widget.R;
-
-/**
- * A {@link Spinner} with settings style.
- *
- * The items in the SettingsSpinner come from the {@link SettingsSpinnerAdapter} associated with
- * this view.
- */
-public class SettingsSpinner extends Spinner {
-
-    /**
-     * Constructs a new SettingsSpinner with the given context's theme.
-     * And it also set a background resource with settings style.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     */
-    public SettingsSpinner(Context context) {
-        super(context);
-        setBackgroundResource(R.drawable.settings_spinner_background);
-    }
-
-    /**
-     * Constructs a new SettingsSpinner with the given context's theme and the supplied
-     * mode of displaying choices. <code>mode</code> may be one of
-     * {@link Spinner#MODE_DIALOG} or {@link Spinner#MODE_DROPDOWN}.
-     * And it also set a background resource with settings style.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param mode Constant describing how the user will select choices from
-     *             the spinner.
-     *
-     * @see Spinner#MODE_DIALOG
-     * @see Spinner#MODE_DROPDOWN
-     */
-    public SettingsSpinner(Context context, int mode) {
-        super(context, mode);
-        setBackgroundResource(R.drawable.settings_spinner_background);
-    }
-
-    /**
-     * Constructs a new SettingsSpinner with the given context's theme and the supplied
-     * attribute set.
-     * And it also set a background resource with settings style.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param attrs The attributes of the XML tag that is inflating the view.
-     */
-    public SettingsSpinner(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setBackgroundResource(R.drawable.settings_spinner_background);
-    }
-
-    /**
-     * Constructs a new SettingsSpinner with the given context's theme, the supplied
-     * attribute set, and default style attribute.
-     * And it also set a background resource with settings style.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param attrs The attributes of the XML tag that is inflating the view.
-     * @param defStyleAttr An attribute in the current theme that contains a
-     *                     reference to a style resource that supplies default
-     *                     values for the view. Can be 0 to not look for
-     *                     defaults.
-     */
-    public SettingsSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        setBackgroundResource(R.drawable.settings_spinner_background);
-    }
-
-    /**
-     * Constructs a new SettingsSpinner with the given context's theme, the supplied
-     * attribute set, and default styles. <code>mode</code> may be one of
-     * {@link Spinner#MODE_DIALOG} or {@link Spinner#MODE_DROPDOWN} and determines how the
-     * user will select choices from the spinner.
-     * And it also set a background resource with settings style.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param attrs The attributes of the XML tag that is inflating the view.
-     * @param defStyleAttr An attribute in the current theme that contains a
-     *                     reference to a style resource that supplies default
-     *                     values for the view. Can be 0 to not look for
-     *                     defaults.
-     * @param defStyleRes A resource identifier of a style resource that
-     *                    supplies default values for the view, used only if
-     *                    defStyleAttr is 0 or can not be found in the theme.
-     *                    Can be 0 to not look for defaults.
-     * @param mode Constant describing how the user will select choices from
-     *             the spinner.
-     *
-     * @see Spinner#MODE_DIALOG
-     * @see Spinner#MODE_DROPDOWN
-     */
-    @RequiresApi(Build.VERSION_CODES.M)
-    public SettingsSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
-            int mode) {
-        super(context, attrs, defStyleAttr, defStyleRes, mode, null);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        setDropDownVerticalOffset(getMeasuredHeight() - (int) getContext().getResources()
-                .getDimension(R.dimen.spinner_padding_top_or_bottom));
-    }
-}
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml
similarity index 93%
rename from packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml
index 0544526..770a69d 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2018 The Android Open Source Project
+  Copyright (C) 2022 The Android Open Source Project
 
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_progress_horizontal.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_progress_horizontal.xml
index a4c780b..ae63928 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_progress_horizontal.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_progress_horizontal.xml
@@ -27,11 +27,12 @@
 
     <item
         android:id="@android:id/progress">
-        <clip>
+        <scale android:scaleWidth="100%" android:useIntrinsicSizeAsMinimum="true">
             <shape>
                 <corners android:radius="8dp" />
                 <solid android:color="?android:attr/textColorPrimary" />
+                <size android:width="8dp"/>
             </shape>
-        </clip>
+        </scale>
     </item>
 </layer-list>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml
new file mode 100644
index 0000000..e1764af
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ripple
+xmlns:android="http://schemas.android.com/apk/res/android"
+android:color="@color/settingslib_ripple_color">
+
+<item android:id="@android:id/background">
+    <layer-list android:paddingMode="stack">
+        <item
+            android:top="8dp"
+            android:bottom="8dp">
+
+            <shape>
+                <corners android:radius="28dp"/>
+                <solid android:color="@android:color/system_accent1_100"/>
+                <size android:height="@dimen/settingslib_spinner_height"/>
+            </shape>
+        </item>
+
+        <item
+            android:gravity="center|end"
+            android:width="18dp"
+            android:height="18dp"
+            android:end="12dp"
+            android:drawable="@drawable/settingslib_arrow_drop_down"/>
+    </layer-list>
+</item>
+</ripple>
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_dropdown_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml
similarity index 75%
rename from packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_dropdown_background.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml
index aa451ae..056fb82 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_dropdown_background.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2018 The Android Open Source Project
+  Copyright (C) 2022 The Android Open Source Project
 
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -17,12 +17,12 @@
 
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:priv-android="http://schemas.android.com/apk/prv/res/android"
-    android:color="@color/ripple_color">
+    android:color="@color/settingslib_ripple_color">
 
     <item android:id="@android:id/background">
         <shape>
-            <solid android:color="?priv-android:attr/colorAccentSecondary"/>
+            <corners android:radius="10dp"/>
+            <solid android:color="@android:color/system_accent2_100"/>
         </shape>
     </item>
 </ripple>
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml
similarity index 80%
copy from packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml
copy to packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml
index 0544526..6ed215d 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2018 The Android Open Source Project
+  Copyright (C) 2022 The Android Open Source Project
 
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -16,11 +16,11 @@
   -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:viewportWidth="18"
-        android:viewportHeight="18"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
         android:width="24dp"
         android:height="24dp">
     <path
         android:pathData="M7 10l5 5 5 -5z"
-        android:fillColor="@android:color/black"/>
+        android:fillColor="?android:attr/textColorPrimary"/>
 </vector>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml
new file mode 100644
index 0000000..7466712
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ripple
+xmlns:android="http://schemas.android.com/apk/res/android"
+android:color="@color/settingslib_ripple_color">
+
+    <item android:id="@android:id/background">
+        <layer-list android:paddingMode="stack">
+            <item
+                android:top="8dp"
+                android:bottom="8dp">
+
+                <shape>
+                    <corners
+                        android:radius="20dp"/>
+                    <solid
+                        android:color="?android:attr/colorPrimary"/>
+                    <stroke
+                        android:color="#1f000000"
+                        android:width="1dp"/>
+                    <size
+                        android:height="32dp"/>
+                </shape>
+            </item>
+
+            <item
+                android:gravity="center|end"
+                android:width="24dp"
+                android:height="24dp"
+                android:end="4dp"
+                android:drawable="@drawable/settingslib_arrow_drop_down"/>
+        </layer-list>
+    </item>
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index 3f2b8ac..77c4533 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -47,4 +47,6 @@
     <color name="settingslib_text_color_secondary_device_default">@android:color/system_neutral2_200</color>
 
     <color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_100</color>
+
+    <color name="settingslib_ripple_color">@color/settingslib_material_grey_900</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index ec3c336..6adb789 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -69,4 +69,7 @@
     <color name="settingslib_text_color_secondary_device_default">@android:color/system_neutral2_700</color>
 
     <color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_600</color>
+
+    <color name="settingslib_ripple_color">?android:attr/colorControlHighlight</color>
+    <color name="settingslib_material_grey_900">#ff212121</color>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
index 11546c8..29fdab1 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
@@ -25,4 +25,6 @@
     <dimen name="settingslib_listPreferredItemPaddingStart">24dp</dimen>
     <!-- Right padding of the preference -->
     <dimen name="settingslib_listPreferredItemPaddingEnd">24dp</dimen>
+
+    <dimen name="settingslib_spinner_height">36dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 8e7226b..9d39911 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -45,4 +45,13 @@
         <item name="android:progressDrawable">@drawable/settingslib_progress_horizontal</item>
         <item name="android:scaleY">0.5</item>
     </style>
+
+    <style name="Spinner.SettingsLib"
+           parent="android:style/Widget.Material.Spinner">
+        <item name="android:background">@drawable/settingslib_spinner_background</item>
+        <item name="android:popupBackground">@drawable/settingslib_spinner_dropdown_background</item>
+        <item name="android:dropDownVerticalOffset">48dp</item>
+        <item name="android:layout_marginTop">16dp</item>
+        <item name="android:layout_marginBottom">8dp</item>
+    </style>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 7bf75bc..e9bbcc78 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -26,6 +26,7 @@
         <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item>
         <item name="android:switchStyle">@style/Switch.SettingsLib</item>
         <item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item>
+        <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item>
     </style>
 
     <!-- Using in SubSettings page including injected settings page -->
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
index aaab0f0..fa27bb6 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -26,4 +26,10 @@
     <style name="TextAppearance.CategoryTitle.SettingsLib"
            parent="@android:style/TextAppearance.DeviceDefault.Medium">
     </style>
+
+    <style name="Spinner.SettingsLib"
+           parent="android:style/Widget.Material.Spinner">
+        <item name="android:background">@drawable/settingslib_spinner_background</item>
+        <item name="android:dropDownVerticalOffset">48dp</item>
+    </style>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
index 2d881d1..8dc0f3c 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
@@ -19,6 +19,7 @@
     <!-- Only using in Settings application -->
     <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
+        <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item>
     </style>
 
     <!-- Using in SubSettings page including injected settings page -->
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
index 53a382a..b81d13d 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
@@ -22,14 +22,12 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.LinearLayout;
+import android.widget.Spinner;
 
 import androidx.preference.PreferenceViewHolder;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.settingslib.widget.settingsspinner.SettingsSpinner;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,7 +40,7 @@
 
     private Context mContext;
     private PreferenceViewHolder mViewHolder;
-    private SettingsSpinner mSpinner;
+    private Spinner mSpinner;
     private SettingsSpinnerPreference mSpinnerPreference;
 
     @Before
@@ -53,7 +51,7 @@
         final View rootView = inflater.inflate(mSpinnerPreference.getLayoutResource(),
                 new LinearLayout(mContext), false /* attachToRoot */);
         mViewHolder = PreferenceViewHolder.createInstanceForTests(rootView);
-        mSpinner = (SettingsSpinner) mViewHolder.findViewById(R.id.spinner);
+        mSpinner = (Spinner) mViewHolder.findViewById(R.id.spinner);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index d7b366e..bb6b293 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -206,9 +206,8 @@
 
         when(config.isMandatoryCodec()).thenReturn(false);
         when(config.getCodecType()).thenReturn(4);
-        when(config.getCodecName()).thenReturn("LDAC");
         assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(
-                String.format(KNOWN_CODEC_LABEL, config.getCodecName()));
+                String.format(KNOWN_CODEC_LABEL, "LDAC"));
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index d53a3e8..298ee90 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -28,6 +28,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothUuid;
 import android.content.Context;
+import android.os.Parcel;
 import android.os.ParcelUuid;
 
 import org.junit.Before;
@@ -60,9 +61,9 @@
     private final static Map<Integer, ParcelUuid> CAP_GROUP2 =
             Map.of(2, BluetoothUuid.CAP);
     private final BluetoothClass DEVICE_CLASS_1 =
-        new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
     private final BluetoothClass DEVICE_CLASS_2 =
-        new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
+            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
     @Mock
     private LocalBluetoothProfileManager mLocalProfileManager;
     @Mock
@@ -92,6 +93,16 @@
     private HearingAidDeviceManager mHearingAidDeviceManager;
     private Context mContext;
 
+    private BluetoothClass createBtClass(int deviceClass) {
+        Parcel p = Parcel.obtain();
+        p.writeInt(deviceClass);
+        p.setDataPosition(0); // reset position of parcel before passing to constructor
+
+        BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
+        p.recycle();
+        return bluetoothClass;
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index 7be176a..a8e6075 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -28,6 +28,7 @@
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.os.Parcel;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -48,7 +49,7 @@
     private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
     private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
     private final BluetoothClass DEVICE_CLASS =
-            new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
+            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
     @Mock
     private LocalBluetoothProfileManager mLocalProfileManager;
     @Mock
@@ -67,6 +68,16 @@
     private HearingAidDeviceManager mHearingAidDeviceManager;
     private Context mContext;
 
+    private BluetoothClass createBtClass(int deviceClass) {
+        Parcel p = Parcel.obtain();
+        p.writeInt(deviceClass);
+        p.setDataPosition(0); // reset position of parcel before passing to constructor
+
+        BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
+        p.recycle();
+        return bluetoothClass;
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 6f7f73a..c122a37 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -31,6 +31,7 @@
 import android.content.Context;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
+import android.os.Parcel;
 
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -65,9 +66,9 @@
     private static final String ROUTER_ID_3 = "RouterId_3";
     private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
     private final BluetoothClass mHeadreeClass =
-            new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
     private final BluetoothClass mCarkitClass =
-            new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO);
+            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO);
 
     @Mock
     private BluetoothDevice mDevice1;
@@ -118,6 +119,16 @@
     private List<MediaDevice> mMediaDevices = new ArrayList<>();
     private PhoneMediaDevice mPhoneMediaDevice;
 
+    private BluetoothClass createBtClass(int deviceClass) {
+        Parcel p = Parcel.obtain();
+        p.writeInt(deviceClass);
+        p.setDataPosition(0); // reset position of parcel before passing to constructor
+
+        BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
+        p.recycle();
+        return bluetoothClass;
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
index 3b7fbc7..c7e96bc 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
@@ -69,7 +69,7 @@
     }
 
     @Implementation
-    protected boolean removeActiveDevice(@BluetoothAdapter.ActiveDeviceUse int profiles) {
+    protected boolean removeActiveDevice(int profiles) {
         if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
                 && profiles != ACTIVE_DEVICE_ALL) {
             return false;
@@ -78,8 +78,7 @@
     }
 
     @Implementation
-    protected boolean setActiveDevice(BluetoothDevice device,
-            @BluetoothAdapter.ActiveDeviceUse int profiles) {
+    protected boolean setActiveDevice(BluetoothDevice device, int profiles) {
         if (device == null) {
             return false;
         }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 741fe4f..1df326a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -536,6 +536,9 @@
     <uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
     <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
     <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
+    <!-- Permission needed for CTS test - ConcurrencyTest#testP2pExternalApprover
+         P2P external approver API sets require MANAGE_WIFI_AUTO_JOIN permission. -->
+    <uses-permission android:name="android.permission.MANAGE_WIFI_AUTO_JOIN" />
 
     <!-- Permission required for CTS tests to enable/disable rate limiting toasts. -->
     <uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" />
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 46adfeb..f7bcf1f 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -39,5 +39,5 @@
     ],
 
     manifest: "AndroidManifest.xml",
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 4540b77..c3f6a5d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -48,9 +48,16 @@
  * nicely into the starting window.
  */
 class ActivityLaunchAnimator(
-    private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS)
+    /** The animator used when animating a View into an app. */
+    private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
+
+    /** The animator used when animating a Dialog into an app. */
+    // TODO(b/218989950): Remove this animator and instead set the duration of the dim fade out to
+    // TIMINGS.contentBeforeFadeOutDuration.
+    private val dialogToAppAnimator: LaunchAnimator = LaunchAnimator(DIALOG_TIMINGS, INTERPOLATORS)
 ) {
     companion object {
+        /** The timings when animating a View into an app. */
         @JvmField
         val TIMINGS = LaunchAnimator.Timings(
             totalDuration = 500L,
@@ -60,6 +67,17 @@
             contentAfterFadeInDuration = 183L
         )
 
+        /**
+         * The timings when animating a Dialog into an app. We need to wait at least 200ms before
+         * showing the app (which is under the dialog window) so that the dialog window dim is fully
+         * faded out, to avoid flicker.
+         */
+        val DIALOG_TIMINGS = TIMINGS.copy(
+            contentBeforeFadeOutDuration = 200L,
+            contentAfterFadeInDelay = 200L
+        )
+
+        /** The interpolators when animating a View or a dialog into an app. */
         val INTERPOLATORS = LaunchAnimator.Interpolators(
             positionInterpolator = Interpolators.EMPHASIZED,
             positionXInterpolator = createPositionXInterpolator(),
@@ -298,10 +316,17 @@
         }
 
         /**
+         * Whether this controller is controlling a dialog launch. This will be used to adapt the
+         * timings, making sure we don't show the app until the dialog dim had the time to fade out.
+         */
+        // TODO(b/218989950): Remove this.
+        val isDialogLaunch: Boolean
+            get() = false
+
+        /**
          * The intent was started. If [willAnimate] is false, nothing else will happen and the
          * animation will not be started.
          */
-        @JvmDefault
         fun onIntentStarted(willAnimate: Boolean) {}
 
         /**
@@ -309,7 +334,6 @@
          * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
          * before the cancellation.
          */
-        @JvmDefault
         fun onLaunchAnimationCancelled() {}
     }
 
@@ -317,7 +341,9 @@
     inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
         private val launchContainer = controller.launchContainer
         private val context = launchContainer.context
-        private val transactionApplier = SyncRtSurfaceTransactionApplier(launchContainer)
+        private val transactionApplierView =
+            controller.openingWindowSyncView ?: controller.launchContainer
+        private val transactionApplier = SyncRtSurfaceTransactionApplier(transactionApplierView)
 
         private val matrix = Matrix()
         private val invertMatrix = Matrix()
@@ -405,6 +431,13 @@
             val callback = this@ActivityLaunchAnimator.callback!!
             val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo)
 
+            // Make sure we use the modified timings when animating a dialog into an app.
+            val launchAnimator = if (controller.isDialogLaunch) {
+                dialogToAppAnimator
+            } else {
+                launchAnimator
+            }
+
             // TODO(b/184121838): We should somehow get the top and bottom radius of the window
             // instead of recomputing isExpandingFullyAbove here.
             val isExpandingFullyAbove =
@@ -440,19 +473,29 @@
                     progress: Float,
                     linearProgress: Float
                 ) {
-                    applyStateToWindow(window, state)
+                    // Apply the state to the window only if it is visible, i.e. when the expanding
+                    // view is *not* visible.
+                    if (!state.visible) {
+                        applyStateToWindow(window, state)
+                    }
                     navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
+
                     listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
                     delegate.onLaunchAnimationProgress(state, progress, linearProgress)
                 }
             }
 
-            // We draw a hole when the additional layer is fading out to reveal the opening window.
             animation = launchAnimator.startAnimation(
                 controller, endState, windowBackgroundColor, drawHole = true)
         }
 
         private fun applyStateToWindow(window: RemoteAnimationTarget, state: LaunchAnimator.State) {
+            if (transactionApplierView.viewRootImpl == null) {
+                // If the view root we synchronize with was detached, don't apply any transaction
+                // (as [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw).
+                return
+            }
+
             val screenBounds = window.screenSpaceBounds
             val centerX = (screenBounds.left + screenBounds.right) / 2f
             val centerY = (screenBounds.top + screenBounds.bottom) / 2f
@@ -510,6 +553,12 @@
             state: LaunchAnimator.State,
             linearProgress: Float
         ) {
+            if (transactionApplierView.viewRootImpl == null) {
+                // If the view root we synchronize with was detached, don't apply any transaction
+                // (as [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw).
+                return
+            }
+
             val fadeInProgress = LaunchAnimator.getProgress(TIMINGS, linearProgress,
                 ANIMATION_DELAY_NAV_FADE_IN, ANIMATION_DURATION_NAV_FADE_OUT)
 
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 3051d80..a3c5649 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -19,7 +19,6 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
-import android.app.ActivityManager
 import android.app.Dialog
 import android.graphics.Color
 import android.graphics.Rect
@@ -28,12 +27,12 @@
 import android.util.Log
 import android.util.MathUtils
 import android.view.GhostView
-import android.view.SurfaceControl
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewRootImpl
+import android.view.WindowInsets
 import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
 import android.widget.FrameLayout
 import kotlin.math.roundToInt
 
@@ -42,12 +41,17 @@
 /**
  * A class that allows dialogs to be started in a seamless way from a view that is transforming
  * nicely into the starting dialog.
+ *
+ * This animator also allows to easily animate a dialog into an activity.
+ *
+ * @see showFromView
+ * @see showFromDialog
+ * @see createActivityLaunchController
  */
 class DialogLaunchAnimator @JvmOverloads constructor(
     private val dreamManager: IDreamManager,
     private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
-    // TODO(b/217621394): Remove special handling for low-RAM devices after animation sync is fixed
-    private var forceDisableSynchronization: Boolean = ActivityManager.isLowRamDeviceStatic()
+    private val isForTesting: Boolean = false
 ) {
     private companion object {
         private val TIMINGS = ActivityLaunchAnimator.TIMINGS
@@ -113,7 +117,7 @@
                 dialog = dialog,
                 animateBackgroundBoundsChange,
                 animatedParent,
-                forceDisableSynchronization
+                isForTesting
         )
 
         openedDialogs.add(animatedDialog)
@@ -141,6 +145,100 @@
     }
 
     /**
+     * Create an [ActivityLaunchAnimator.Controller] that can be used to launch an activity from the
+     * dialog that contains [View]. Note that the dialog must have been show using [showFromView]
+     * and be currently showing, otherwise this will return null.
+     *
+     * The returned controller will take care of dismissing the dialog at the right time after the
+     * activity started, when the dialog to app animation is done (or when it is cancelled). If this
+     * method returns null, then the dialog won't be dismissed.
+     *
+     * @param view any view inside the dialog to animate.
+     */
+    @JvmOverloads
+    fun createActivityLaunchController(
+        view: View,
+        cujType: Int? = null
+    ): ActivityLaunchAnimator.Controller? {
+        val animatedDialog = openedDialogs
+            .firstOrNull { it.dialog.window.decorView.viewRootImpl == view.viewRootImpl }
+            ?: return null
+
+        // At this point, we know that the intent of the caller is to dismiss the dialog to show
+        // an app, so we disable the exit animation into the touch surface because we will never
+        // want to run it anyways.
+        animatedDialog.exitAnimationDisabled = true
+
+        val dialog = animatedDialog.dialog
+
+        // Don't animate if the dialog is not showing.
+        if (!dialog.isShowing) {
+            return null
+        }
+
+        val dialogContentWithBackground = animatedDialog.dialogContentWithBackground ?: return null
+        val controller =
+            ActivityLaunchAnimator.Controller.fromView(dialogContentWithBackground, cujType)
+                ?: return null
+
+        // Wrap the controller into one that will instantly dismiss the dialog when the animation is
+        // done or dismiss it normally (fading it out) if the animation is cancelled.
+        return object : ActivityLaunchAnimator.Controller by controller {
+            override val isDialogLaunch = true
+
+            override fun onIntentStarted(willAnimate: Boolean) {
+                controller.onIntentStarted(willAnimate)
+
+                if (!willAnimate) {
+                    dialog.dismiss()
+                }
+            }
+
+            override fun onLaunchAnimationCancelled() {
+                controller.onLaunchAnimationCancelled()
+                enableDialogDismiss()
+                dialog.dismiss()
+            }
+
+            override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+                controller.onLaunchAnimationStart(isExpandingFullyAbove)
+
+                // Make sure the dialog is not dismissed during the animation.
+                disableDialogDismiss()
+
+                // If this dialog was shown from a cascade of other dialogs, make sure those ones
+                // are dismissed too.
+                animatedDialog.touchSurface = animatedDialog.prepareForStackDismiss()
+
+                // Remove the dim.
+                dialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+            }
+
+            override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+                controller.onLaunchAnimationEnd(isExpandingFullyAbove)
+
+                // Hide the dialog then dismiss it to instantly dismiss it without playing the
+                // animation.
+                dialog.hide()
+                enableDialogDismiss()
+                dialog.dismiss()
+            }
+
+            private fun disableDialogDismiss() {
+                dialog.setDismissOverride { /* Do nothing */ }
+            }
+
+            private fun enableDialogDismiss() {
+                // We don't set the override to null given that [AnimatedDialog.OnDialogDismissed]
+                // will still properly dismiss the dialog but will also make sure to clean up
+                // everything (like making sure that the touched view that triggered the dialog is
+                // made VISIBLE again).
+                dialog.setDismissOverride(animatedDialog::onDialogDismissed)
+            }
+        }
+    }
+
+    /**
      * Ensure that all dialogs currently shown won't animate into their touch surface when
      * dismissed.
      *
@@ -358,6 +456,21 @@
         // Make sure the dialog is visible instantly and does not do any window animation.
         window.attributes.windowAnimations = R.style.Animation_LaunchAnimation
 
+        // Ensure that the animation is not clipped by the display cut-out when animating this
+        // dialog into an app.
+        window.attributes.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+        window.attributes = window.attributes
+
+        // We apply the insets ourselves to make sure that the paddings are set on the correct
+        // View.
+        window.setDecorFitsSystemWindows(false)
+        val viewWithInsets = (dialogContentWithBackground.parent as ViewGroup)
+        viewWithInsets.setOnApplyWindowInsetsListener { view, windowInsets ->
+            val insets = windowInsets.getInsets(WindowInsets.Type.displayCutout())
+            view.setPadding(insets.left, insets.top, insets.right, insets.bottom)
+            WindowInsets.CONSUMED
+        }
+
         // Start the animation once the background view is properly laid out.
         dialogContentWithBackground.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
             override fun onLayoutChange(
@@ -421,45 +534,12 @@
      * (or inversely, removed from the UI when the touch surface is made visible).
      */
     private fun synchronizeNextDraw(then: () -> Unit) {
-        if (forceDisableSynchronization ||
-                !touchSurface.isAttachedToWindow || touchSurface.viewRootImpl == null ||
-                !decorView.isAttachedToWindow || decorView.viewRootImpl == null) {
-            // No need to synchronize if either the touch surface or dialog view is not attached
-            // to a window.
+        if (forceDisableSynchronization) {
             then()
             return
         }
 
-        // Consume the next frames of both view roots to make sure the ghost view is drawn at
-        // exactly the same time as when the touch surface is made invisible.
-        var remainingTransactions = 0
-        val mergedTransactions = SurfaceControl.Transaction()
-
-        fun onTransaction(transaction: SurfaceControl.Transaction?) {
-            remainingTransactions--
-            transaction?.let { mergedTransactions.merge(it) }
-
-            if (remainingTransactions == 0) {
-                mergedTransactions.apply()
-                then()
-            }
-        }
-
-        fun consumeNextDraw(viewRootImpl: ViewRootImpl) {
-            if (viewRootImpl.consumeNextDraw(::onTransaction)) {
-                remainingTransactions++
-
-                // Make sure we trigger a traversal.
-                viewRootImpl.view.invalidate()
-            }
-        }
-
-        consumeNextDraw(touchSurface.viewRootImpl)
-        consumeNextDraw(decorView.viewRootImpl)
-
-        if (remainingTransactions == 0) {
-            then()
-        }
+        ViewRootSync.synchronizeNextDraw(touchSurface, decorView, then)
     }
 
     private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
@@ -523,7 +603,7 @@
         )
     }
 
-    private fun onDialogDismissed() {
+    fun onDialogDismissed() {
         if (Looper.myLooper() != Looper.getMainLooper()) {
             dialog.context.mainExecutor.execute { onDialogDismissed() }
             return
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
index 77386cf..a4c5c30 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
@@ -77,8 +77,8 @@
          * This will be used to:
          *  - Get the associated [Context].
          *  - Compute whether we are expanding fully above the launch container.
-         *  - Apply surface transactions in sync with RenderThread when animating an activity
-         *    launch.
+         *  - Get to overlay to which we initially put the window background layer, until the
+         *    opening window is made visible (see [openingWindowSyncView]).
          *
          * This container can be changed to force this [Controller] to animate the expanding view
          * inside a different location, for instance to ensure correct layering during the
@@ -87,6 +87,18 @@
         var launchContainer: ViewGroup
 
         /**
+         * The [View] with which the opening app window should be synchronized with once it starts
+         * to be visible.
+         *
+         * We will also move the window background layer to this view's overlay once the opening
+         * window is visible.
+         *
+         * If null, this will default to [launchContainer].
+         */
+        val openingWindowSyncView: View?
+            get() = null
+
+        /**
          * Return the [State] of the view that will be animated. We will animate from this state to
          * the final window state.
          *
@@ -100,11 +112,9 @@
          * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding
          * fully above the [launchContainer].
          */
-        @JvmDefault
         fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {}
 
         /** The animation made progress and the expandable view [state] should be updated. */
-        @JvmDefault
         fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {}
 
         /**
@@ -112,7 +122,6 @@
          * called previously. This is typically used to clean up the resources initialized when the
          * animation was started.
          */
-        @JvmDefault
         fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {}
     }
 
@@ -154,7 +163,7 @@
     }
 
     /** The timings (durations and delays) used by this animator. */
-    class Timings(
+    data class Timings(
         /** The total duration of the animation. */
         val totalDuration: Long,
 
@@ -257,8 +266,17 @@
         animator.duration = timings.totalDuration
         animator.interpolator = LINEAR
 
+        // Whether we should move the [windowBackgroundLayer] into the overlay of
+        // [Controller.openingWindowSyncView] once the opening app window starts to be visible.
+        val openingWindowSyncView = controller.openingWindowSyncView
+        val openingWindowSyncViewOverlay = openingWindowSyncView?.overlay
+        val moveBackgroundLayerWhenAppIsVisible = openingWindowSyncView != null &&
+            openingWindowSyncView.viewRootImpl != controller.launchContainer.viewRootImpl
+
         val launchContainerOverlay = launchContainer.overlay
         var cancelled = false
+        var movedBackgroundLayer = false
+
         animator.addListener(object : AnimatorListenerAdapter() {
             override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
                 if (DEBUG) {
@@ -278,6 +296,10 @@
                 }
                 controller.onLaunchAnimationEnd(isExpandingFullyAbove)
                 launchContainerOverlay.remove(windowBackgroundLayer)
+
+                if (moveBackgroundLayerWhenAppIsVisible) {
+                    openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+                }
             }
         })
 
@@ -318,11 +340,29 @@
                 timings.contentBeforeFadeOutDuration
             ) < 1
 
+            if (moveBackgroundLayerWhenAppIsVisible && !state.visible && !movedBackgroundLayer) {
+                // The expanding view is not visible, so the opening app is visible. If this is the
+                // first frame when it happens, trigger a one-off sync and move the background layer
+                // in its new container.
+                movedBackgroundLayer = true
+
+                launchContainerOverlay.remove(windowBackgroundLayer)
+                openingWindowSyncViewOverlay!!.add(windowBackgroundLayer)
+
+                ViewRootSync.synchronizeNextDraw(launchContainer, openingWindowSyncView, then = {})
+            }
+
+            val container = if (movedBackgroundLayer) {
+                openingWindowSyncView!!
+            } else {
+                controller.launchContainer
+            }
+
             applyStateToWindowBackgroundLayer(
                 windowBackgroundLayer,
                 state,
                 linearProgress,
-                launchContainer,
+                container,
                 drawHole
             )
             controller.onLaunchAnimationProgress(state, progress, linearProgress)
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt
new file mode 100644
index 0000000..5b3e45c
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt
@@ -0,0 +1,75 @@
+package com.android.systemui.animation
+
+import android.app.ActivityManager
+import android.view.SurfaceControl
+import android.view.View
+import android.view.ViewRootImpl
+
+/** A util class to synchronize 2 view roots. */
+// TODO(b/200284684): Remove this class.
+object ViewRootSync {
+    // TODO(b/217621394): Remove special handling for low-RAM devices after animation sync is fixed
+    private val forceDisableSynchronization = ActivityManager.isLowRamDeviceStatic()
+
+    /**
+     * Synchronize the next draw between the view roots of [view] and [otherView], then run [then].
+     *
+     * Note that in some cases, the synchronization might not be possible (e.g. WM consumed the
+     * next transactions) or disabled (temporarily, on low ram devices). In this case, [then] will
+     * be called without synchronizing.
+     */
+    fun synchronizeNextDraw(
+        view: View,
+        otherView: View,
+        then: () -> Unit
+    ) {
+        if (forceDisableSynchronization ||
+            !view.isAttachedToWindow || view.viewRootImpl == null ||
+            !otherView.isAttachedToWindow || otherView.viewRootImpl == null ||
+            view.viewRootImpl == otherView.viewRootImpl) {
+            // No need to synchronize if either the touch surface or dialog view is not attached
+            // to a window.
+            then()
+            return
+        }
+
+        // Consume the next frames of both view roots to make sure the ghost view is drawn at
+        // exactly the same time as when the touch surface is made invisible.
+        var remainingTransactions = 0
+        val mergedTransactions = SurfaceControl.Transaction()
+
+        fun onTransaction(transaction: SurfaceControl.Transaction?) {
+            remainingTransactions--
+            transaction?.let { mergedTransactions.merge(it) }
+
+            if (remainingTransactions == 0) {
+                mergedTransactions.apply()
+                then()
+            }
+        }
+
+        fun consumeNextDraw(viewRootImpl: ViewRootImpl) {
+            if (viewRootImpl.consumeNextDraw(::onTransaction)) {
+                remainingTransactions++
+
+                // Make sure we trigger a traversal.
+                viewRootImpl.view.invalidate()
+            }
+        }
+
+        consumeNextDraw(view.viewRootImpl)
+        consumeNextDraw(otherView.viewRootImpl)
+
+        if (remainingTransactions == 0) {
+            then()
+        }
+    }
+
+    /**
+     * A Java-friendly API for [synchronizeNextDraw].
+     */
+    @JvmStatic
+    fun synchronizeNextDraw(view: View, otherView: View, then: Runnable) {
+        synchronizeNextDraw(view, otherView, then::run)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_chevron_icon.xml b/packages/SystemUI/res/drawable/ic_chevron_icon.xml
new file mode 100644
index 0000000..acbbbcb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_chevron_icon.xml
@@ -0,0 +1,28 @@
+
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="18dp"
+    android:height="31dp"
+    android:viewportWidth="18"
+    android:viewportHeight="31">
+  <path
+      android:pathData="M0.0061,27.8986L2.6906,30.5831L17.9219,15.3518L2.6906,0.1206L0.0061,2.8051L12.5338,15.3518"
+      android:strokeAlpha="0.7"
+      android:fillColor="#FFFFFF"
+      android:fillAlpha="0.7"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 5d80da8..e1dbe69 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -113,4 +113,16 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"/>
 
+    <ImageView
+        android:id="@+id/chevron_icon"
+        android:autoMirrored="true"
+        android:src="@drawable/ic_chevron_icon"
+        android:visibility="invisible"
+        android:layout_width="@dimen/control_chevron_icon_size"
+        android:layout_height="@dimen/control_chevron_icon_size"
+        android:clickable="false"
+        android:focusable="false"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"/>
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/idle_host_view.xml b/packages/SystemUI/res/layout/idle_host_view.xml
deleted file mode 100644
index f407874..0000000
--- a/packages/SystemUI/res/layout/idle_host_view.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-
-<com.android.systemui.idle.IdleHostView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/idle_host_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 39d7f4f..c7910afc 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -89,11 +89,6 @@
             layout="@layout/keyguard_status_view"
             android:visibility="gone"/>
 
-        <include layout="@layout/idle_host_view"
-                 android:layout_width="match_parent"
-                 android:layout_height="match_parent"
-                 android:visibility="gone"/>
-
         <include layout="@layout/dock_info_overlay"/>
 
         <FrameLayout
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index b318bbc..f1e93c2 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -67,10 +67,10 @@
 
     <!-- media output dialog-->
     <color name="media_dialog_background">@android:color/system_neutral1_900</color>
-    <color name="media_dialog_active_item_main_content">@android:color/system_accent2_800</color>
-    <color name="media_dialog_inactive_item_main_content">@android:color/system_accent1_100</color>
-    <color name="media_dialog_item_status">@android:color/system_accent1_100</color>
-    <color name="media_dialog_item_background">@android:color/system_neutral2_800</color>
+    <color name="media_dialog_active_item_main_content">@android:color/system_neutral1_900</color>
+    <color name="media_dialog_inactive_item_main_content">@android:color/system_neutral1_900</color>
+    <color name="media_dialog_item_status">@android:color/system_neutral1_900</color>
+    <color name="media_dialog_item_background">@android:color/system_accent2_50</color>
 
     <!-- Biometric dialog colors -->
     <color name="biometric_dialog_gray">#ffcccccc</color>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index faf518e..cbda439 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -155,6 +155,7 @@
     <color name="GM2_grey_900">#202124</color>
 
     <color name="GM2_red_300">#F28B82</color>
+    <color name="GM2_red_500">#EA4335</color>
     <color name="GM2_red_700">#C5221F</color>
 
     <color name="GM2_blue_300">#8AB4F8</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index bb1ffa8..178f93a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -608,10 +608,6 @@
     <!-- Component name of communal source service -->
     <string name="config_communalSourceComponent" translatable="false">@null</string>
 
-    <!-- Whether idle mode should be enabled. When enabled, the lock screen will timeout to an idle
-         screen on inactivity. -->
-    <bool name="config_enableIdleMode">false</bool>
-
     <!-- This value is used when calculating whether the device is in ambient light mode. It is
         light mode when the light sensor sample value exceeds above this value. -->
     <integer name="config_ambientLightModeThreshold">5</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a08d824..3704134 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1038,6 +1038,7 @@
     <dimen name="control_spinner_padding_horizontal">20dp</dimen>
     <dimen name="control_text_size">14sp</dimen>
     <dimen name="control_icon_size">24dp</dimen>
+    <dimen name="control_chevron_icon_size">20dp</dimen>
     <dimen name="control_spacing">8dp</dimen>
     <dimen name="control_list_divider">1dp</dimen>
     <dimen name="control_corner_radius">14dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b983545..09ae3f9 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -399,6 +399,11 @@
         <!-- that would otherwise be intercepted by the Shade. -->
         <item name="android:windowFullscreen">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
+
+        <!-- Empty enter/exit animation, we will animate in-window. Note that the implementation -->
+        <!-- of ActionsDialogLite relies on this to be null (resource=0) to detect when to run -->
+        <!-- the in-window animation. -->
+        <item name="android:windowAnimationStyle">@null</item>
     </style>
 
     <style name="QSBorderlessButton">
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 11fffd0..f5084f5 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -21,6 +21,7 @@
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -28,6 +29,7 @@
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dagger.WMComponent;
+import com.android.wm.shell.dagger.WMShellConcurrencyModule;
 import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
 import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
 import com.android.wm.shell.transition.ShellTransitions;
@@ -96,8 +98,9 @@
                 && android.os.Process.myUserHandle().isSystem()
                 && ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName());
         mRootComponent = buildGlobalRootComponent(context);
+
         // Stand up WMComponent
-        mWMComponent = mRootComponent.getWMComponentBuilder().build();
+        setupWmComponent(context);
         if (mInitializeComponents) {
             // Only initialize when not starting from tests since this currently initializes some
             // components that shouldn't be run in the test environment
@@ -124,8 +127,8 @@
                     .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
                     .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
                     .setRecentTasks(mWMComponent.getRecentTasks())
-                    .setCompatUI(Optional.of(mWMComponent.getCompatUI()))
-                    .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()))
+                    .setCompatUI(mWMComponent.getCompatUI())
+                    .setDragAndDrop(mWMComponent.getDragAndDrop())
                     .setBackAnimation(mWMComponent.getBackAnimation());
         } else {
             // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
@@ -161,6 +164,36 @@
     }
 
     /**
+     * Sets up {@link #mWMComponent}. On devices where the Shell runs on its own main thread,
+     * this will pre-create the thread to ensure that the components are constructed on the
+     * same thread, to reduce the likelihood of side effects from running the constructors on
+     * a different thread than the rest of the class logic.
+     */
+    private void setupWmComponent(Context context) {
+        WMComponent.Builder wmBuilder = mRootComponent.getWMComponentBuilder();
+        if (!mInitializeComponents || !WMShellConcurrencyModule.enableShellMainThread(context)) {
+            // If running under tests or shell thread is not enabled, we don't need anything special
+            mWMComponent = wmBuilder.build();
+            return;
+        }
+
+        // If the shell main thread is enabled, initialize the component on that thread
+        HandlerThread shellThread = WMShellConcurrencyModule.createShellMainThread();
+        shellThread.start();
+
+        // Use an async handler since we don't care about synchronization
+        Handler shellHandler = Handler.createAsync(shellThread.getLooper());
+        boolean built = shellHandler.runWithScissors(() -> {
+            wmBuilder.setShellMainThread(shellThread);
+            mWMComponent = wmBuilder.build();
+        }, 5000);
+        if (!built) {
+            Log.w(TAG, "Failed to initialize WMComponent");
+            throw new RuntimeException();
+        }
+    }
+
+    /**
      * Prepares the SysUIComponent builder before it is built.
      * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method
      * @param wm the built WMComponent from the root component's getWMComponent() method
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java
deleted file mode 100644
index 2d59e13..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.communal.conditions;
-
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.wifi.WifiInfo;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.condition.Condition;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.util.Arrays;
-import java.util.HashSet;
-
-import javax.inject.Inject;
-
-/**
- * Monitors Wi-Fi connections and triggers callback, if any, when the device is connected to and
- * disconnected from a trusted network.
- */
-public class CommunalTrustedNetworkCondition extends Condition {
-    private final String mTag = getClass().getSimpleName();
-    private final ConnectivityManager mConnectivityManager;
-    private final ContentObserver mTrustedNetworksObserver;
-    private final SecureSettings mSecureSettings;
-
-    // The SSID of the connected Wi-Fi network. Null if not connected to Wi-Fi.
-    private String mWifiSSID;
-
-    // Set of SSIDs of trusted networks.
-    private final HashSet<String> mTrustedNetworks = new HashSet<>();
-
-    /**
-     * The deliminator used to separate trusted network keys saved as a string in secure settings.
-     */
-    public static final String SETTINGS_STRING_DELIMINATOR = ",/";
-
-    @Inject
-    public CommunalTrustedNetworkCondition(@Main Handler handler,
-            ConnectivityManager connectivityManager, SecureSettings secureSettings) {
-        mConnectivityManager = connectivityManager;
-        mSecureSettings = secureSettings;
-
-        mTrustedNetworksObserver = new ContentObserver(handler) {
-            @Override
-            public void onChange(boolean selfChange) {
-                fetchTrustedNetworks();
-            }
-        };
-    }
-
-    /**
-     * Starts monitoring for trusted network connection. Ignores if already started.
-     */
-    @Override
-    protected void start() {
-        if (shouldLog()) Log.d(mTag, "start listening for wifi connections");
-
-        fetchTrustedNetworks();
-
-        final NetworkRequest wifiNetworkRequest = new NetworkRequest.Builder().addTransportType(
-                NetworkCapabilities.TRANSPORT_WIFI).build();
-        mConnectivityManager.registerNetworkCallback(wifiNetworkRequest, mNetworkCallback);
-        mSecureSettings.registerContentObserverForUser(
-                Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS, false, mTrustedNetworksObserver,
-                UserHandle.USER_SYSTEM);
-    }
-
-    /**
-     * Stops monitoring for trusted network connection.
-     */
-    @Override
-    protected void stop() {
-        if (shouldLog()) Log.d(mTag, "stop listening for wifi connections");
-
-        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
-        mSecureSettings.unregisterContentObserver(mTrustedNetworksObserver);
-    }
-
-    private void updateWifiInfo(WifiInfo wifiInfo) {
-        if (wifiInfo == null) {
-            mWifiSSID = null;
-        } else {
-            // Remove the wrapping quotes around the SSID.
-            mWifiSSID = wifiInfo.getSSID().replace("\"", "");
-        }
-
-        checkIfConnectedToTrustedNetwork();
-    }
-
-    private void fetchTrustedNetworks() {
-        final String trustedNetworksString = mSecureSettings.getStringForUser(
-                Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS, UserHandle.USER_SYSTEM);
-        mTrustedNetworks.clear();
-
-        if (shouldLog()) Log.d(mTag, "fetched trusted networks: " + trustedNetworksString);
-
-        if (TextUtils.isEmpty(trustedNetworksString)) {
-            return;
-        }
-
-        mTrustedNetworks.addAll(
-                Arrays.asList(trustedNetworksString.split(SETTINGS_STRING_DELIMINATOR)));
-
-        checkIfConnectedToTrustedNetwork();
-    }
-
-    private void checkIfConnectedToTrustedNetwork() {
-        final boolean connectedToTrustedNetwork = mWifiSSID != null && mTrustedNetworks.contains(
-                mWifiSSID);
-
-        if (shouldLog()) {
-            Log.d(mTag, (connectedToTrustedNetwork ? "connected to" : "disconnected from")
-                    + " a trusted network");
-        }
-
-        updateCondition(connectedToTrustedNetwork);
-    }
-
-    private final ConnectivityManager.NetworkCallback mNetworkCallback =
-            new ConnectivityManager.NetworkCallback(
-                    ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
-                private boolean mIsConnected = false;
-                private WifiInfo mWifiInfo;
-
-                @Override
-                public void onAvailable(@NonNull Network network) {
-                    super.onAvailable(network);
-
-                    if (shouldLog()) Log.d(mTag, "connected to wifi");
-
-                    mIsConnected = true;
-                    if (mWifiInfo != null) {
-                        updateWifiInfo(mWifiInfo);
-                    }
-                }
-
-                @Override
-                public void onLost(@NonNull Network network) {
-                    super.onLost(network);
-
-                    if (shouldLog()) Log.d(mTag, "disconnected from wifi");
-
-                    mIsConnected = false;
-                    mWifiInfo = null;
-                    updateWifiInfo(null);
-                }
-
-                @Override
-                public void onCapabilitiesChanged(@NonNull Network network,
-                        @NonNull NetworkCapabilities networkCapabilities) {
-                    super.onCapabilitiesChanged(network, networkCapabilities);
-
-                    mWifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
-
-                    if (mIsConnected) {
-                        updateWifiInfo(mWifiInfo);
-                    }
-                }
-            };
-
-    private boolean shouldLog() {
-        return Log.isLoggable(mTag, Log.DEBUG);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
index e1f1ac4..814b251 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -20,8 +20,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.text.TextUtils;
-import android.view.View;
-import android.widget.FrameLayout;
 
 import androidx.annotation.Nullable;
 
@@ -30,9 +28,6 @@
 import com.android.systemui.communal.PackageObserver;
 import com.android.systemui.communal.conditions.CommunalSettingCondition;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.idle.AmbientLightModeMonitor;
-import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
-import com.android.systemui.idle.dagger.IdleViewComponent;
 import com.android.systemui.util.condition.Condition;
 import com.android.systemui.util.condition.Monitor;
 import com.android.systemui.util.condition.dagger.MonitorComponent;
@@ -46,7 +41,6 @@
 import javax.inject.Named;
 import javax.inject.Provider;
 
-import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.ElementsIntoSet;
@@ -58,22 +52,12 @@
  */
 @Module(subcomponents = {
         CommunalViewComponent.class,
-        IdleViewComponent.class,
 })
 public interface CommunalModule {
-    String IDLE_VIEW = "idle_view";
     String COMMUNAL_CONDITIONS = "communal_conditions";
 
     /** */
     @Provides
-    @Named(IDLE_VIEW)
-    static View provideIdleView(Context context) {
-        FrameLayout view = new FrameLayout(context);
-        return view;
-    }
-
-    /** */
-    @Provides
     static Optional<CommunalSource.Observer> provideCommunalSourcePackageObserver(
             Context context, @Main Resources resources) {
         final String componentName = resources.getString(R.string.config_communalSourceComponent);
@@ -87,15 +71,6 @@
     }
 
     /**
-     * Provides LightSensorEventsDebounceAlgorithm as an instance to DebounceAlgorithm interface.
-     * @param algorithm the instance of algorithm that is bound to the interface.
-     * @return the interface that is bound to.
-     */
-    @Binds
-    AmbientLightModeMonitor.DebounceAlgorithm ambientLightDebounceAlgorithm(
-            LightSensorEventsDebounceAlgorithm algorithm);
-
-    /**
      * Provides a set of conditions that need to be fulfilled in order for Communal Mode to display.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 47e749c..4819bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -118,6 +118,7 @@
     private var nextStatusText: CharSequence = ""
     val title: TextView = layout.requireViewById(R.id.title)
     val subtitle: TextView = layout.requireViewById(R.id.subtitle)
+    val chevronIcon: ImageView = layout.requireViewById(R.id.chevron_icon)
     val context: Context = layout.getContext()
     val clipLayer: ClipDrawable
     lateinit var cws: ControlWithState
@@ -163,6 +164,7 @@
             cws.control?.let {
                 title.setText(it.title)
                 subtitle.setText(it.subtitle)
+                chevronIcon.visibility = if (usePanel()) View.VISIBLE else View.INVISIBLE
             }
         }
 
@@ -469,6 +471,7 @@
         updateContentDescription()
 
         status.setTextColor(color)
+        chevronIcon.imageTintList = color
 
         control?.getCustomIcon()?.let {
             icon.setImageIcon(it)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index b926692..b02074a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -17,6 +17,9 @@
 package com.android.systemui.dagger;
 
 import android.content.Context;
+import android.os.HandlerThread;
+
+import androidx.annotation.Nullable;
 
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.tv.TvWMComponent;
@@ -26,6 +29,7 @@
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.dagger.TvWMShellModule;
 import com.android.wm.shell.dagger.WMShellModule;
@@ -44,6 +48,7 @@
 
 import java.util.Optional;
 
+import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
@@ -64,6 +69,10 @@
      */
     @Subcomponent.Builder
     interface Builder {
+
+        @BindsInstance
+        Builder setShellMainThread(@Nullable @ShellMainThread HandlerThread t);
+
         WMComponent build();
     }
 
@@ -120,10 +129,10 @@
     Optional<RecentTasks> getRecentTasks();
 
     @WMSingleton
-    CompatUI getCompatUI();
+    Optional<CompatUI> getCompatUI();
 
     @WMSingleton
-    DragAndDrop getDragAndDrop();
+    Optional<DragAndDrop> getDragAndDrop();
 
     @WMSingleton
     Optional<BackAnimation> getBackAnimation();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index befb648..789ad62 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -118,9 +118,11 @@
             switch (this) {
                 case UNINITIALIZED:
                 case INITIALIZED:
-                case DOZE_REQUEST_PULSE:
                     return parameters.shouldControlScreenOff() ? Display.STATE_ON
                             : Display.STATE_OFF;
+                case DOZE_REQUEST_PULSE:
+                    return parameters.getDisplayNeedsBlanking() ? Display.STATE_OFF
+                            : Display.STATE_ON;
                 case DOZE_AOD_PAUSED:
                 case DOZE:
                     return Display.STATE_OFF;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index f0371fc..84fa6a6 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -31,6 +31,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Dialog;
@@ -76,8 +77,10 @@
 import android.view.IWindowManager;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -109,6 +112,7 @@
 import com.android.systemui.MultiListLayout;
 import com.android.systemui.MultiListLayout.MultiListAdapter;
 import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.animation.Interpolators;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -2170,6 +2174,7 @@
         private Optional<StatusBar> mStatusBarOptional;
         private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
         private LockPatternUtils mLockPatternUtils;
+        private float mWindowDimAmount;
 
         protected ViewGroup mContainer;
 
@@ -2251,6 +2256,7 @@
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             initializeLayout();
+            mWindowDimAmount = getWindow().getAttributes().dimAmount;
         }
 
         @Override
@@ -2459,6 +2465,96 @@
             mNotificationShadeWindowController.setRequestTopUi(true, TAG);
             mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
                     .commitUpdate(mContext.getDisplayId());
+
+            // By default this dialog windowAnimationStyle is null, and therefore windowAnimations
+            // should be equal to 0 which means we need to animate the dialog in-window. If it's not
+            // equal to 0, it means it has been overridden to animate (e.g. by the
+            // DialogLaunchAnimator) so we don't run the animation.
+            boolean shouldAnimateInWindow = getWindow().getAttributes().windowAnimations == 0;
+            if (shouldAnimateInWindow) {
+                startAnimation(true /* isEnter */, null /* then */);
+
+                // Override the dialog dismiss so that we can animate in-window before dismissing
+                // the dialog.
+                setDismissOverride(() -> {
+                    startAnimation(false /* isEnter */, /* then */ () -> {
+                        setDismissOverride(null);
+
+                        // Hide then dismiss to instantly dismiss.
+                        hide();
+                        dismiss();
+                    });
+                });
+            }
+        }
+
+        /** Run either the enter or exit animation, then run {@code then}. */
+        private void startAnimation(boolean isEnter, @Nullable Runnable then) {
+            ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+
+            // Note: these specs should be the same as in popup_enter_material and
+            // popup_exit_material.
+            float translationPx;
+            Resources resources = getContext().getResources();
+            if (isEnter) {
+                translationPx = resources.getDimension(R.dimen.popup_enter_animation_from_y_delta);
+                animator.setInterpolator(Interpolators.STANDARD);
+                animator.setDuration(resources.getInteger(R.integer.config_activityDefaultDur));
+            } else {
+                translationPx = resources.getDimension(R.dimen.popup_exit_animation_to_y_delta);
+                animator.setInterpolator(Interpolators.STANDARD_ACCELERATE);
+                animator.setDuration(resources.getInteger(R.integer.config_activityShortDur));
+            }
+
+            Window window = getWindow();
+            int rotation = window.getWindowManager().getDefaultDisplay().getRotation();
+
+            animator.addUpdateListener(valueAnimator -> {
+                float progress = (float) valueAnimator.getAnimatedValue();
+
+                float alpha = isEnter ? progress : 1 - progress;
+                mGlobalActionsLayout.setAlpha(alpha);
+                window.setDimAmount(mWindowDimAmount * alpha);
+
+                // TODO(b/213872558): Support devices that don't have their power button on the
+                // right.
+                float translation =
+                        isEnter ? translationPx * (1 - progress) : translationPx * progress;
+                switch (rotation) {
+                    case Surface.ROTATION_0:
+                        mGlobalActionsLayout.setTranslationX(translation);
+                        break;
+                    case Surface.ROTATION_90:
+                        mGlobalActionsLayout.setTranslationY(-translation);
+                        break;
+                    case Surface.ROTATION_180:
+                        mGlobalActionsLayout.setTranslationX(-translation);
+                        break;
+                    case Surface.ROTATION_270:
+                        mGlobalActionsLayout.setTranslationY(translation);
+                        break;
+                }
+            });
+
+            animator.addListener(new AnimatorListenerAdapter() {
+                private int mPreviousLayerType;
+
+                @Override
+                public void onAnimationStart(Animator animation, boolean isReverse) {
+                    mPreviousLayerType = mGlobalActionsLayout.getLayerType();
+                    mGlobalActionsLayout.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mGlobalActionsLayout.setLayerType(mPreviousLayerType, null);
+                    if (then != null) {
+                        then.run();
+                    }
+                }
+            });
+
+            animator.start();
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt b/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt
deleted file mode 100644
index fa00dbb..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.idle
-
-import android.annotation.IntDef
-import android.hardware.Sensor
-import android.hardware.SensorEvent
-import android.hardware.SensorEventListener
-import android.hardware.SensorManager
-import android.util.Log
-import com.android.systemui.util.sensors.AsyncSensorManager
-import javax.inject.Inject
-
-/**
- * Monitors ambient light signals, applies a debouncing algorithm, and produces the current
- * ambient light mode.
- *
- * @property algorithm the debounce algorithm which transforms light sensor events into an
- * ambient light mode.
- * @property sensorManager the sensor manager used to register sensor event updates.
- */
-class AmbientLightModeMonitor @Inject constructor(
-    private val algorithm: DebounceAlgorithm,
-    private val sensorManager: AsyncSensorManager
-) {
-    companion object {
-        private const val TAG = "AmbientLightModeMonitor"
-        private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
-
-        const val AMBIENT_LIGHT_MODE_LIGHT = 0
-        const val AMBIENT_LIGHT_MODE_DARK = 1
-        const val AMBIENT_LIGHT_MODE_UNDECIDED = 2
-    }
-
-    // Light sensor used to detect ambient lighting conditions.
-    private val lightSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
-
-    // Represents all ambient light modes.
-    @Retention(AnnotationRetention.SOURCE)
-    @IntDef(AMBIENT_LIGHT_MODE_LIGHT, AMBIENT_LIGHT_MODE_DARK, AMBIENT_LIGHT_MODE_UNDECIDED)
-    annotation class AmbientLightMode
-
-    /**
-     * Start monitoring the current ambient light mode.
-     *
-     * @param callback callback that gets triggered when the ambient light mode changes.
-     */
-    fun start(callback: Callback) {
-        if (DEBUG) Log.d(TAG, "start monitoring ambient light mode")
-
-        if (lightSensor == null) {
-            if (DEBUG) Log.w(TAG, "light sensor not available")
-            return
-        }
-
-        algorithm.start(callback)
-        sensorManager.registerListener(mSensorEventListener, lightSensor,
-                SensorManager.SENSOR_DELAY_NORMAL)
-    }
-
-    /**
-     * Stop monitoring the current ambient light mode.
-     */
-    fun stop() {
-        if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode")
-
-        algorithm.stop()
-        sensorManager.unregisterListener(mSensorEventListener)
-    }
-
-    private val mSensorEventListener: SensorEventListener = object : SensorEventListener {
-        override fun onSensorChanged(event: SensorEvent) {
-            if (event.values.isEmpty()) {
-                if (DEBUG) Log.w(TAG, "SensorEvent doesn't have any value")
-                return
-            }
-
-            algorithm.onUpdateLightSensorEvent(event.values[0])
-        }
-
-        override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
-            // Do nothing.
-        }
-    }
-
-    /**
-     * Interface of the ambient light mode callback, which gets triggered when the mode changes.
-     */
-    interface Callback {
-        fun onChange(@AmbientLightMode mode: Int)
-    }
-
-    /**
-     * Interface of the algorithm that transforms light sensor events to an ambient light mode.
-     */
-    interface DebounceAlgorithm {
-        // Setting Callback to nullable so mockito can verify without throwing NullPointerException.
-        fun start(callback: Callback?)
-        fun stop()
-        fun onUpdateLightSensorEvent(value: Float)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostView.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostView.java
deleted file mode 100644
index 7d79279..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/IdleHostView.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.idle;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * {@link IdleHostView} houses a surface to be displayed when the device idle.
- */
-public class IdleHostView extends FrameLayout {
-    public IdleHostView(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public IdleHostView(@NonNull Context context,
-            @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public IdleHostView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
deleted file mode 100644
index 624d01f..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.idle;
-
-import static com.android.systemui.communal.dagger.CommunalModule.IDLE_VIEW;
-
-import android.annotation.IntDef;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.ViewController;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Provider;
-
-/**
- * {@link IdleHostViewController} processes signals to control the lifecycle of the idle screen.
- */
-public class IdleHostViewController extends ViewController<IdleHostView> {
-    private static final String TAG = "IdleHostViewController";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @IntDef({STATE_IDLE_MODE_ENABLED, STATE_KEYGUARD_SHOWING, STATE_DOZING, STATE_DREAMING,
-            STATE_LOW_LIGHT})
-    public @interface State {}
-
-    // Set at construction to indicate idle mode is available.
-    private static final int STATE_IDLE_MODE_ENABLED = 1 << 0;
-
-    // Set when keyguard is present, even below a dream or AOD. AKA the device is locked.
-    private static final int STATE_KEYGUARD_SHOWING = 1 << 1;
-
-    // Set when the device has entered a dozing / low power state.
-    private static final int STATE_DOZING = 1 << 2;
-
-    // Set when the device has entered a dreaming state, which includes dozing.
-    private static final int STATE_DREAMING = 1 << 3;
-
-    // Set when the device is in a low light environment.
-    private static final int STATE_LOW_LIGHT = 1 << 4;
-
-    // The aggregate current state.
-    private int mState;
-    private boolean mIsMonitoringLowLight;
-    private boolean mIsMonitoringDream;
-
-    private final BroadcastDispatcher mBroadcastDispatcher;
-
-    private final PowerManager mPowerManager;
-
-    // Keyguard state controller for monitoring keyguard show state.
-    private final KeyguardStateController mKeyguardStateController;
-
-    // Status bar state controller for monitoring when the device is dozing.
-    private final StatusBarStateController mStatusBarStateController;
-
-    // Intent filter for receiving dream broadcasts.
-    private IntentFilter mDreamIntentFilter;
-
-    // Monitor for the current ambient light mode. Used to trigger / exit low-light mode.
-    private final AmbientLightModeMonitor mAmbientLightModeMonitor;
-
-    private final KeyguardStateController.Callback mKeyguardCallback =
-            new KeyguardStateController.Callback() {
-                @Override
-                public void onKeyguardShowingChanged() {
-                    setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
-                }
-            };
-
-    private final StatusBarStateController.StateListener mStatusBarCallback =
-            new StatusBarStateController.StateListener() {
-                @Override
-                public void onDozingChanged(boolean isDozing) {
-                    setState(STATE_DOZING, isDozing);
-                }
-            };
-
-    private final BroadcastReceiver mDreamStateReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) {
-                setState(STATE_DREAMING, true);
-            } else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
-                setState(STATE_DREAMING, false);
-            }
-        }
-    };
-
-    private final AmbientLightModeMonitor.Callback mAmbientLightModeCallback =
-            mode -> {
-                boolean shouldBeLowLight;
-                switch (mode) {
-                    case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED:
-                        return;
-                    case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT:
-                        shouldBeLowLight = false;
-                        break;
-                    case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK:
-                        shouldBeLowLight = true;
-                        break;
-                    default:
-                        Log.w(TAG, "invalid ambient light mode");
-                        return;
-                }
-
-                if (DEBUG) Log.d(TAG, "ambient light mode changed to " + mode);
-
-                final boolean isLowLight = getState(STATE_LOW_LIGHT);
-                if (shouldBeLowLight != isLowLight) {
-                    setState(STATE_LOW_LIGHT, shouldBeLowLight);
-                }
-            };
-
-    final Provider<View> mIdleViewProvider;
-
-    @Inject
-    protected IdleHostViewController(
-            BroadcastDispatcher broadcastDispatcher,
-            PowerManager powerManager,
-            IdleHostView view,
-            @Main Resources resources,
-            @Named(IDLE_VIEW) Provider<View> idleViewProvider,
-            KeyguardStateController keyguardStateController,
-            StatusBarStateController statusBarStateController,
-            AmbientLightModeMonitor ambientLightModeMonitor) {
-        super(view);
-        mBroadcastDispatcher = broadcastDispatcher;
-        mPowerManager = powerManager;
-        mIdleViewProvider = idleViewProvider;
-        mKeyguardStateController = keyguardStateController;
-        mStatusBarStateController = statusBarStateController;
-        mAmbientLightModeMonitor = ambientLightModeMonitor;
-
-        mState = STATE_KEYGUARD_SHOWING;
-
-        final boolean enabled = resources.getBoolean(R.bool.config_enableIdleMode);
-        if (enabled) {
-            mState |= STATE_IDLE_MODE_ENABLED;
-        }
-
-        setState(mState, true);
-
-        if (DEBUG) {
-            Log.d(TAG, "initial state:" + mState + " enabled:" + enabled);
-        }
-    }
-
-    @Override
-    public void init() {
-        super.init();
-
-        setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
-        setState(STATE_DOZING, mStatusBarStateController.isDozing());
-    }
-
-    private void setState(@State int state, boolean active) {
-        final int oldState = mState;
-
-        if (active) {
-            mState |= state;
-        } else {
-            mState &= ~state;
-        }
-
-        if (oldState == mState) {
-            return;
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "set " + getStateName(state) + " to " + active);
-            logCurrentState();
-        }
-
-        final boolean inCommunalMode = getState(STATE_IDLE_MODE_ENABLED)
-                && getState(STATE_KEYGUARD_SHOWING);
-
-        enableDreamMonitoring(inCommunalMode);
-        enableLowLightMonitoring(inCommunalMode);
-
-        if (state == STATE_LOW_LIGHT) {
-            enableLowLightMode(inCommunalMode && active);
-        }
-    }
-
-    private void enableDreamMonitoring(boolean enable) {
-        if (mIsMonitoringDream == enable) {
-            return;
-        }
-
-        mIsMonitoringDream = enable;
-
-        if (DEBUG) {
-            Log.d(TAG, (enable ? "enable" : "disable") + " dream monitoring");
-        }
-
-        if (mDreamIntentFilter == null) {
-            mDreamIntentFilter = new IntentFilter();
-            mDreamIntentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
-            mDreamIntentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
-        }
-
-        if (enable) {
-            mBroadcastDispatcher.registerReceiver(mDreamStateReceiver, mDreamIntentFilter);
-        } else {
-            mBroadcastDispatcher.unregisterReceiver(mDreamStateReceiver);
-        }
-    }
-
-    private void enableLowLightMonitoring(boolean enable) {
-        if (enable == mIsMonitoringLowLight) {
-            return;
-        }
-
-        mIsMonitoringLowLight = enable;
-
-        if (mIsMonitoringLowLight) {
-            if (DEBUG) Log.d(TAG, "enable low light monitoring");
-            mAmbientLightModeMonitor.start(mAmbientLightModeCallback);
-        } else {
-            if (DEBUG) Log.d(TAG, "disable low light monitoring");
-            mAmbientLightModeMonitor.stop();
-        }
-    }
-
-    private void enableLowLightMode(boolean enable) {
-        if (enable == getState(STATE_DOZING)) {
-            return;
-        }
-
-        if (enable) {
-            if (DEBUG) Log.d(TAG, "enter low light, start dozing");
-
-            mPowerManager.goToSleep(
-                    SystemClock.uptimeMillis(),
-                    PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
-        } else {
-            if (DEBUG) Log.d(TAG, "exit low light, stop dozing");
-            mPowerManager.wakeUp(SystemClock.uptimeMillis(),
-                    PowerManager.WAKE_REASON_APPLICATION, "Exit low light condition");
-        }
-    }
-
-    @Override
-    protected void onViewAttached() {
-        if (DEBUG) {
-            Log.d(TAG, "onViewAttached");
-        }
-
-        mKeyguardStateController.addCallback(mKeyguardCallback);
-        mStatusBarStateController.addCallback(mStatusBarCallback);
-    }
-
-    @Override
-    protected void onViewDetached() {
-        mKeyguardStateController.removeCallback(mKeyguardCallback);
-        mStatusBarStateController.removeCallback(mStatusBarCallback);
-    }
-
-    private String getStateName(@State int state) {
-        switch (state) {
-            case STATE_IDLE_MODE_ENABLED:
-                return "STATE_IDLE_MODE_ENABLED";
-            case STATE_KEYGUARD_SHOWING:
-                return "STATE_KEYGUARD_SHOWING";
-            case STATE_DOZING:
-                return "STATE_DOZING";
-            case STATE_DREAMING:
-                return "STATE_DREAMING";
-            case STATE_LOW_LIGHT:
-                return "STATE_LOW_LIGHT";
-            default:
-                return "STATE_UNKNOWN";
-        }
-    }
-
-    private boolean getState(@State int state) {
-        return getState(state, mState);
-    }
-
-    private boolean getState(@State int state, int oldState) {
-        return (oldState & state) == state;
-    }
-
-    private String getStateLog(@State int state) {
-        return getStateName(state) + " = " + getState(state);
-    }
-
-    private void logCurrentState() {
-        Log.d(TAG, "current state: {\n"
-                + "\t" + getStateLog(STATE_IDLE_MODE_ENABLED) + "\n"
-                + "\t" + getStateLog(STATE_KEYGUARD_SHOWING) + "\n"
-                + "\t" + getStateLog(STATE_DOZING) + "\n"
-                + "\t" + getStateLog(STATE_DREAMING) + "\n"
-                + "\t" + getStateLog(STATE_LOW_LIGHT) + "\n"
-                + "}");
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/InputMonitorFactory.java b/packages/SystemUI/src/com/android/systemui/idle/InputMonitorFactory.java
deleted file mode 100644
index be0a378..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/InputMonitorFactory.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.idle;
-
-import com.android.systemui.dagger.qualifiers.DisplayId;
-import com.android.systemui.shared.system.InputMonitorCompat;
-
-import javax.inject.Inject;
-
-/**
- * {@link InputMonitorFactory} generates instances of {@link InputMonitorCompat}.
- */
-public class InputMonitorFactory {
-    private final int mDisplayId;
-
-    @Inject
-    public InputMonitorFactory(@DisplayId int displayId) {
-        mDisplayId = displayId;
-    }
-
-    /**
-     * Generates a new {@link InputMonitorCompat}.
-     *
-     * @param identifier Identifier to generate monitor with.
-     * @return A {@link InputMonitorCompat} instance.
-     */
-    public InputMonitorCompat getInputMonitor(String identifier) {
-        return new InputMonitorCompat(identifier, mDisplayId);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/LightSensorEventsDebounceAlgorithm.kt b/packages/SystemUI/src/com/android/systemui/idle/LightSensorEventsDebounceAlgorithm.kt
deleted file mode 100644
index 7b06b96..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/LightSensorEventsDebounceAlgorithm.kt
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.idle
-
-import android.content.res.Resources
-import android.util.Log
-import com.android.systemui.R
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.util.concurrency.DelayableExecutor
-import javax.inject.Inject
-
-/**
- * An algorithm that receives light sensor events, debounces the signals, and transforms into an
- * ambient light mode: light, dark, or undecided.
- *
- * More about the algorithm at go/titan-light-sensor-debouncer.
- */
-class LightSensorEventsDebounceAlgorithm @Inject constructor(
-    @Main private val executor: DelayableExecutor,
-    @Main resources: Resources
-) : AmbientLightModeMonitor.DebounceAlgorithm {
-    companion object {
-        private const val TAG = "LightDebounceAlgorithm"
-        private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
-    }
-
-    // The ambient mode is considered light mode when the light sensor value increases exceeding
-    // this value.
-    private val lightModeThreshold =
-            resources.getInteger(R.integer.config_ambientLightModeThreshold)
-
-    // The ambient mode is considered dark mode when the light sensor value drops below this
-    // value.
-    private val darkModeThreshold = resources.getInteger(R.integer.config_ambientDarkModeThreshold)
-
-    // Each sample for calculating light mode contains light sensor events collected for this
-    // duration of time in milliseconds.
-    private val lightSamplingSpanMillis =
-            resources.getInteger(R.integer.config_ambientLightModeSamplingSpanMillis)
-
-    // Each sample for calculating dark mode contains light sensor events collected for this
-    // duration of time in milliseconds.
-    private val darkSamplingSpanMillis =
-            resources.getInteger(R.integer.config_ambientDarkModeSamplingSpanMillis)
-
-    // The calculations for light mode is performed at this frequency in milliseconds.
-    private val lightSamplingFrequencyMillis =
-            resources.getInteger(R.integer.config_ambientLightModeSamplingFrequencyMillis)
-
-    // The calculations for dark mode is performed at this frequency in milliseconds.
-    private val darkSamplingFrequencyMillis =
-            resources.getInteger(R.integer.config_ambientDarkModeSamplingFrequencyMillis)
-
-    // Registered callback, which gets triggered when the ambient light mode changes.
-    private var callback: AmbientLightModeMonitor.Callback? = null
-
-    // Queue of bundles used for calculating [isLightMode], ordered from oldest to latest.
-    val bundlesQueueLightMode = ArrayList<ArrayList<Float>>()
-
-    // Queue of bundles used for calculating [isDarkMode], ordered from oldest to latest
-    val bundlesQueueDarkMode = ArrayList<ArrayList<Float>>()
-
-    // The current ambient light mode.
-    @AmbientLightModeMonitor.AmbientLightMode
-    var mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
-        set(value) {
-            if (field == value) return
-            field = value
-
-            if (DEBUG) Log.d(TAG, "ambient light mode changed to $value")
-
-            callback?.onChange(value)
-        }
-
-    // The latest claim of whether it should be light mode.
-    var isLightMode = false
-        set(value) {
-            if (field == value) return
-            field = value
-
-            if (DEBUG) Log.d(TAG, "isLightMode: $value")
-
-            mode = when {
-                isDarkMode -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
-                value -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
-                else -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
-            }
-        }
-
-    // The latest claim of whether it should be dark mode.
-    var isDarkMode = false
-        set(value) {
-            if (field == value) return
-            field = value
-
-            if (DEBUG) Log.d(TAG, "isDarkMode: $value")
-
-            mode = when {
-                value -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
-                isLightMode -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
-                else -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
-            }
-        }
-
-    // The latest average of the light mode bundle.
-    var bundleAverageLightMode = 0.0
-        set(value) {
-            field = value
-
-            if (DEBUG) Log.d(TAG, "light mode average: $value")
-
-            isLightMode = value > lightModeThreshold
-        }
-
-    // The latest average of the dark mode bundle.
-    var bundleAverageDarkMode = 0.0
-        set(value) {
-            field = value
-
-            if (DEBUG) Log.d(TAG, "dark mode average: $value")
-
-            isDarkMode = value < darkModeThreshold
-        }
-
-    // The latest bundle for calculating light mode claim.
-    var bundleLightMode = ArrayList<Float>()
-        set(value) {
-            field = value
-
-            val average = value.average()
-
-            if (!average.isNaN()) {
-                bundleAverageLightMode = average
-            }
-        }
-
-    // The latest bundle for calculating dark mode claim.
-    var bundleDarkMode = ArrayList<Float>()
-        set(value) {
-            field = value
-
-            val average = value.average()
-
-            if (!average.isNaN()) {
-                bundleAverageDarkMode = average
-            }
-        }
-
-    // The latest light level from light sensor event updates.
-    var lightSensorLevel = 0.0f
-        set(value) {
-            field = value
-
-            bundlesQueueLightMode.forEach { bundle -> bundle.add(value) }
-            bundlesQueueDarkMode.forEach { bundle -> bundle.add(value) }
-        }
-
-    // Creates a new bundle that collects light sensor events for calculating the light mode claim,
-    // and adds it to the end of the queue. It schedules a call to dequeue this bundle after
-    // [LIGHT_SAMPLING_SPAN_MILLIS]. Once started, it also repeatedly calls itself at
-    // [LIGHT_SAMPLING_FREQUENCY_MILLIS].
-    val enqueueLightModeBundle: Runnable = object : Runnable {
-        override fun run() {
-            if (DEBUG) Log.d(TAG, "enqueueing a light mode bundle")
-
-            bundlesQueueLightMode.add(ArrayList())
-
-            executor.executeDelayed(dequeueLightModeBundle, lightSamplingSpanMillis.toLong())
-            executor.executeDelayed(this, lightSamplingFrequencyMillis.toLong())
-        }
-    }
-
-    // Creates a new bundle that collects light sensor events for calculating the dark mode claim,
-    // and adds it to the end of the queue. It schedules a call to dequeue this bundle after
-    // [DARK_SAMPLING_SPAN_MILLIS]. Once started, it also repeatedly calls itself at
-    // [DARK_SAMPLING_FREQUENCY_MILLIS].
-    val enqueueDarkModeBundle: Runnable = object : Runnable {
-        override fun run() {
-            if (DEBUG) Log.d(TAG, "enqueueing a dark mode bundle")
-
-            bundlesQueueDarkMode.add(ArrayList())
-
-            executor.executeDelayed(dequeueDarkModeBundle, darkSamplingSpanMillis.toLong())
-            executor.executeDelayed(this, darkSamplingFrequencyMillis.toLong())
-        }
-    }
-
-    // Collects the oldest bundle from the light mode bundles queue, and as a result triggering a
-    // calculation of the light mode claim.
-    val dequeueLightModeBundle: Runnable = object : Runnable {
-        override fun run() {
-            if (bundlesQueueLightMode.isEmpty()) return
-
-            bundleLightMode = bundlesQueueLightMode.removeAt(0)
-
-            if (DEBUG) Log.d(TAG, "dequeued a light mode bundle of size ${bundleLightMode.size}")
-        }
-    }
-
-    // Collects the oldest bundle from the dark mode bundles queue, and as a result triggering a
-    // calculation of the dark mode claim.
-    val dequeueDarkModeBundle: Runnable = object : Runnable {
-        override fun run() {
-            if (bundlesQueueDarkMode.isEmpty()) return
-
-            bundleDarkMode = bundlesQueueDarkMode.removeAt(0)
-
-            if (DEBUG) Log.d(TAG, "dequeued a dark mode bundle of size ${bundleDarkMode.size}")
-        }
-    }
-
-    /**
-     * Start the algorithm.
-     *
-     * @param callback callback that gets triggered when the ambient light mode changes.
-     */
-    override fun start(callback: AmbientLightModeMonitor.Callback?) {
-        if (DEBUG) Log.d(TAG, "start algorithm")
-
-        if (callback == null) {
-            if (DEBUG) Log.w(TAG, "callback is null")
-            return
-        }
-
-        if (this.callback != null) {
-            if (DEBUG) Log.w(TAG, "already started")
-            return
-        }
-
-        this.callback = callback
-
-        executor.execute(enqueueLightModeBundle)
-        executor.execute(enqueueDarkModeBundle)
-    }
-
-    /**
-     * Stop the algorithm.
-     */
-    override fun stop() {
-        if (DEBUG) Log.d(TAG, "stop algorithm")
-
-        if (callback == null) {
-            if (DEBUG) Log.w(TAG, "haven't started")
-            return
-        }
-
-        callback = null
-
-        // Resets bundle queues.
-        bundlesQueueLightMode.clear()
-        bundlesQueueDarkMode.clear()
-
-        // Resets ambient light mode.
-        mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
-    }
-
-    /**
-     * Update the light sensor event value.
-     *
-     * @param value light sensor update value.
-     */
-    override fun onUpdateLightSensorEvent(value: Float) {
-        if (callback == null) {
-            if (DEBUG) Log.w(TAG, "ignore light sensor event because algorithm not started")
-            return
-        }
-
-        lightSensorLevel = value
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/idle/dagger/IdleViewComponent.java b/packages/SystemUI/src/com/android/systemui/idle/dagger/IdleViewComponent.java
deleted file mode 100644
index 9754b1f..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/dagger/IdleViewComponent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.idle.dagger;
-
-import com.android.systemui.idle.IdleHostView;
-import com.android.systemui.idle.IdleHostViewController;
-
-import dagger.BindsInstance;
-import dagger.Subcomponent;
-
-/**
- * Subcomponent for working with {@link IdleHostView}.
- */
-@Subcomponent
-public interface IdleViewComponent {
-    /** Simple factory for {@link Factory}. */
-    @Subcomponent.Factory
-    interface Factory {
-        IdleViewComponent build(@BindsInstance IdleHostView idleHostView);
-    }
-
-    /** Builds a {@link IdleHostViewController}. */
-    IdleHostViewController getIdleHostViewController();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 2dff947..c3b4354 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -16,12 +16,7 @@
 
 package com.android.systemui.media.dagger;
 
-import android.content.Context;
-import android.os.Handler;
-import android.view.WindowManager;
-
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaFlags;
 import com.android.systemui.media.MediaHierarchyManager;
@@ -34,11 +29,8 @@
 import com.android.systemui.media.taptotransfer.MediaTttFlags;
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
 import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.commandline.CommandRegistry;
 
 import java.util.Optional;
-import java.util.concurrent.Executor;
 
 import javax.inject.Named;
 
@@ -101,13 +93,11 @@
     @SysUISingleton
     static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
             MediaTttFlags mediaTttFlags,
-            CommandQueue commandQueue,
-            Context context,
-            WindowManager windowManager) {
+            Lazy<MediaTttChipControllerSender> controllerSenderLazy) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(new MediaTttChipControllerSender(commandQueue, context, windowManager));
+        return Optional.of(controllerSenderLazy.get());
     }
 
     /** */
@@ -115,16 +105,11 @@
     @SysUISingleton
     static Optional<MediaTttChipControllerReceiver> providesMediaTttChipControllerReceiver(
             MediaTttFlags mediaTttFlags,
-            CommandQueue commandQueue,
-            Context context,
-            WindowManager windowManager,
-            @Main Handler mainHandler) {
+            Lazy<MediaTttChipControllerReceiver> controllerReceiverLazy) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(
-                new MediaTttChipControllerReceiver(
-                        commandQueue, context, windowManager, mainHandler));
+        return Optional.of(controllerReceiverLazy.get());
     }
 
     /** */
@@ -132,14 +117,11 @@
     @SysUISingleton
     static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
             MediaTttFlags mediaTttFlags,
-            CommandRegistry commandRegistry,
-            Context context,
-            @Main Executor mainExecutor) {
+            Lazy<MediaTttCommandLineHelper> helperLazy) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(
-                new MediaTttCommandLineHelper(commandRegistry, context, mainExecutor));
+        return Optional.of(helperLazy.get());
     }
 
     /** */
@@ -147,13 +129,12 @@
     @SysUISingleton
     static Optional<MediaMuteAwaitConnectionCli> providesMediaMuteAwaitConnectionCli(
             MediaFlags mediaFlags,
-            CommandRegistry commandRegistry,
-            Context context
+            Lazy<MediaMuteAwaitConnectionCli> muteAwaitConnectionCliLazy
     ) {
         if (!mediaFlags.areMuteAwaitConnectionsEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(new MediaMuteAwaitConnectionCli(commandRegistry, context));
+        return Optional.of(muteAwaitConnectionCliLazy.get());
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 5b29719..3cd3905 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -103,6 +103,7 @@
             }
             mCheckBox.setVisibility(View.GONE);
             mStatusIcon.setVisibility(View.GONE);
+            mContainerLayout.setOnClickListener(null);
             mTitleText.setTextColor(mController.getColorInactiveItem());
             mSeekBar.getProgressDrawable().setColorFilter(
                     new PorterDuffColorFilter(mController.getColorSeekbarProgress(),
@@ -151,6 +152,7 @@
                     setSingleLineLayout(getItemTitle(device), true /* bFocused */,
                             true /* showSeekBar */,
                             false /* showProgressBar */, false /* showStatus */);
+                    mCheckBox.setOnCheckedChangeListener(null);
                     mCheckBox.setVisibility(View.VISIBLE);
                     mCheckBox.setChecked(true);
                     mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
@@ -169,6 +171,7 @@
                     initSeekbar(device);
                     mCurrentActivePosition = position;
                 } else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+                    mCheckBox.setOnCheckedChangeListener(null);
                     mCheckBox.setVisibility(View.VISIBLE);
                     mCheckBox.setChecked(false);
                     mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
@@ -204,7 +207,7 @@
                 d.setColorFilter(new PorterDuffColorFilter(
                         Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
                 mTitleIcon.setImageDrawable(d);
-                mContainerLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
+                mContainerLayout.setOnClickListener(mController::launchBluetoothPairing);
             }
         }
 
@@ -241,11 +244,5 @@
                 notifyDataSetChanged();
             }
         }
-
-        private void onItemClick(int customizedItem) {
-            if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
-                mController.launchBluetoothPairing();
-            }
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 85d1795..1f11d0c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -177,6 +177,9 @@
                                     .mutate() : mContext.getDrawable(
                             R.drawable.media_output_item_background)
                             .mutate();
+            backgroundDrawable.setColorFilter(new PorterDuffColorFilter(
+                    mController.getColorItemBackground(),
+                    PorterDuff.Mode.SRC_IN));
             mItemLayout.setBackground(backgroundDrawable);
             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
             mSeekBar.setAlpha(1);
@@ -212,8 +215,13 @@
             mStatusIcon.setVisibility(showStatus ? View.VISIBLE : View.GONE);
             mSeekBar.setAlpha(1);
             mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
-            mItemLayout.setBackground(mContext.getDrawable(R.drawable.media_output_item_background)
-                    .mutate());
+            final Drawable backgroundDrawable = mContext.getDrawable(
+                            R.drawable.media_output_item_background)
+                    .mutate();
+            backgroundDrawable.setColorFilter(new PorterDuffColorFilter(
+                    mController.getColorItemBackground(),
+                    PorterDuff.Mode.SRC_IN));
+            mItemLayout.setBackground(backgroundDrawable);
             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
             mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
             mTwoLineTitleText.setTranslationY(0);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index e141cccc..7bc0f52 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -59,6 +59,7 @@
 import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.monet.ColorScheme;
 import com.android.systemui.plugins.ActivityStarter;
@@ -111,6 +112,7 @@
     private int mColorInactiveItem;
     private int mColorSeekbarProgress;
     private int mColorButtonBackground;
+    private int mColorItemBackground;
 
     @Inject
     public MediaOutputController(@NonNull Context context, String packageName,
@@ -142,6 +144,8 @@
                 android.R.color.system_accent1_200);
         mColorButtonBackground = Utils.getColorStateListDefaultColor(mContext,
                 R.color.media_dialog_item_background);
+        mColorItemBackground = Utils.getColorStateListDefaultColor(mContext,
+                android.R.color.system_accent2_50);
     }
 
     void start(@NonNull Callback cb) {
@@ -350,14 +354,16 @@
                 isDarkTheme);
         if (isDarkTheme) {
             mColorActiveItem = mCurrentColorScheme.getNeutral1().get(10);
-            mColorInactiveItem = mCurrentColorScheme.getAccent1().get(2);
-            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(3);
+            mColorInactiveItem = mCurrentColorScheme.getNeutral1().get(10);
+            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(2);
             mColorButtonBackground = mCurrentColorScheme.getAccent1().get(2);
+            mColorItemBackground = mCurrentColorScheme.getAccent2().get(0);
         } else {
             mColorActiveItem = mCurrentColorScheme.getNeutral1().get(10);
             mColorInactiveItem = mCurrentColorScheme.getAccent1().get(7);
             mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(3);
-            mColorButtonBackground = mCurrentColorScheme.getAccent2().get(1);
+            mColorButtonBackground = mCurrentColorScheme.getAccent1().get(3);
+            mColorItemBackground = mCurrentColorScheme.getAccent2().get(0);
         }
     }
 
@@ -377,6 +383,10 @@
         return mColorButtonBackground;
     }
 
+    public int getColorItemBackground() {
+        return mColorItemBackground;
+    }
+
     private void buildMediaDevices(List<MediaDevice> devices) {
         // For the first time building list, to make sure the top device is the connected device.
         if (mMediaDevices.isEmpty()) {
@@ -568,12 +578,14 @@
         return false;
     }
 
-    void launchBluetoothPairing() {
-        // Dismissing a dialog into its touch surface and starting an activity at the same time
-        // looks bad, so let's make sure the dialog just fades out quickly.
-        mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
+    void launchBluetoothPairing(View view) {
+        ActivityLaunchAnimator.Controller controller =
+                mDialogLaunchAnimator.createActivityLaunchController(view);
 
-        mCallback.dismissDialog();
+        if (controller == null) {
+            mCallback.dismissDialog();
+        }
+
         Intent launchIntent =
                 new Intent(ACTION_BLUETOOTH_PAIRING_SETTINGS)
                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -588,10 +600,10 @@
             deepLinkIntent.putExtra(
                     Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
                     PAGE_CONNECTED_DEVICES_KEY);
-            mActivityStarter.startActivity(deepLinkIntent, true);
+            mActivityStarter.startActivity(deepLinkIntent, true, controller);
             return;
         }
-        mActivityStarter.startActivity(launchIntent, true);
+        mActivityStarter.startActivity(launchIntent, true, controller);
     }
 
     void launchMediaOutputGroupDialog(View mediaOutputDialog) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 4993105..ee2fba0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -25,8 +25,11 @@
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
+import androidx.annotation.VisibleForTesting
 import com.android.internal.widget.CachingIconView
 import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.DelayableExecutor
 
 /**
  * A superclass controller that provides common functionality for showing chips on the sender device
@@ -38,6 +41,7 @@
 abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
     internal val context: Context,
     private val windowManager: WindowManager,
+    @Main private val mainExecutor: DelayableExecutor,
     @LayoutRes private val chipLayoutRes: Int
 ) {
     /** The window layout parameters we'll use when attaching the view to a window. */
@@ -56,6 +60,9 @@
     /** The chip view currently being displayed. Null if the chip is not being displayed. */
     var chipView: ViewGroup? = null
 
+    /** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
+    var cancelChipViewTimeout: Runnable? = null
+
     /**
      * Displays the chip with the current state.
      *
@@ -77,8 +84,11 @@
         if (oldChipView == null) {
             windowManager.addView(chipView, windowLayoutParams)
         }
-    }
 
+        // Cancel and re-set the chip timeout each time we get a new state.
+        cancelChipViewTimeout?.run()
+        cancelChipViewTimeout = mainExecutor.executeDelayed(this::removeChip, TIMEOUT_MILLIS)
+    }
 
     /** Hides the chip. */
     fun removeChip() {
@@ -118,3 +128,5 @@
 // Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
 // UpdateMediaTapToTransferReceiverDisplayTest
 private const val WINDOW_TITLE = "Media Transfer Chip View"
+@VisibleForTesting
+const val TIMEOUT_MILLIS = 3000L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 18623c0..214a888 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -18,7 +18,6 @@
 
 import android.app.StatusBarManager
 import android.content.Context
-import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
 import android.media.MediaRoute2Info
 import android.os.Handler
@@ -27,10 +26,10 @@
 import android.view.WindowManager
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.DelayableExecutor
 import javax.inject.Inject
 
 /**
@@ -43,9 +42,10 @@
     commandQueue: CommandQueue,
     context: Context,
     windowManager: WindowManager,
+    mainExecutor: DelayableExecutor,
     @Main private val mainHandler: Handler,
 ) : MediaTttChipControllerCommon<ChipStateReceiver>(
-    context, windowManager, R.layout.media_ttt_chip_receiver
+    context, windowManager, mainExecutor, R.layout.media_ttt_chip_receiver
 ) {
     private val commandQueueCallbacks = object : CommandQueue.Callbacks {
         override fun updateMediaTapToTransferReceiverDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index da767ea..482e604 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -27,8 +27,10 @@
 import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.DelayableExecutor
 import javax.inject.Inject
 
 /**
@@ -40,8 +42,9 @@
     commandQueue: CommandQueue,
     context: Context,
     windowManager: WindowManager,
+    @Main private val mainExecutor: DelayableExecutor,
 ) : MediaTttChipControllerCommon<ChipStateSender>(
-    context, windowManager, R.layout.media_ttt_chip
+    context, windowManager, mainExecutor,  R.layout.media_ttt_chip
 ) {
     private val commandQueueCallbacks = object : CommandQueue.Callbacks {
         override fun updateMediaTapToTransferSenderDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index d16c019..8b39e5c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -542,7 +542,7 @@
         return mNavigationBarView;
     }
 
-    public View createView(Bundle savedState) {
+    public View createView(Bundle savedState, boolean initialVisibility) {
         mFrame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
                 R.layout.navigation_bar_window, null);
         View barView = LayoutInflater.from(mFrame.getContext()).inflate(
@@ -550,6 +550,8 @@
         barView.addOnAttachStateChangeListener(this);
         mNavigationBarView = barView.findViewById(R.id.navigation_bar_view);
 
+        mNavigationBarView.setVisibility(initialVisibility ? View.VISIBLE : View.INVISIBLE);
+
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView);
         mWindowManager.addView(mFrame,
                 getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index aa1117c..a049736 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -59,6 +59,7 @@
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
@@ -85,6 +86,7 @@
     private final NavigationBar.Factory mNavigationBarFactory;
     private final DisplayManager mDisplayManager;
     private final TaskbarDelegate mTaskbarDelegate;
+    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private int mNavMode;
     @VisibleForTesting boolean mIsTablet;
 
@@ -108,6 +110,7 @@
             NavBarHelper navBarHelper,
             TaskbarDelegate taskbarDelegate,
             NavigationBar.Factory navigationBarFactory,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             DumpManager dumpManager,
             AutoHideController autoHideController,
             LightBarController lightBarController,
@@ -122,6 +125,7 @@
         mConfigChanges.applyNewConfig(mContext.getResources());
         mNavMode = navigationModeController.addListener(this);
         mTaskbarDelegate = taskbarDelegate;
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
                 navBarHelper, navigationModeController, sysUiFlagsContainer,
                 dumpManager, autoHideController, lightBarController, pipOptional,
@@ -323,7 +327,8 @@
 
         mNavigationBars.put(displayId, navBar);
 
-        View navigationBarView = navBar.createView(savedState);
+        boolean navBarVisible = mStatusBarKeyguardViewManager.isNavBarVisible();
+        View navigationBarView = navBar.createView(savedState, navBarVisible);
         navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 9199911..9ea2763 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -263,15 +263,6 @@
                     // Notify FalsingManager that an intentional gesture has occurred.
                     // TODO(b/186519446): use a different method than isFalseTouch
                     mFalsingManager.isFalseTouch(BACK_GESTURE);
-                    // Only inject back keycodes when ahead-of-time back dispatching is disabled.
-                    if (mBackAnimation == null) {
-                        boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
-                        boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
-                        if (DEBUG_MISSING_GESTURE) {
-                            Log.d(DEBUG_MISSING_GESTURE_TAG, "Triggered back: down="
-                                    + sendDown + ", up=" + sendUp);
-                        }
-                    }
 
                     mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
                             (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 17f85ee..3c7933f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -381,14 +381,17 @@
                 : View.INVISIBLE);
         mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (expanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
-        boolean footerVisible = !mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating
+        boolean qsPanelVisible = !mQsDisabled && expandVisually;
+        boolean footerVisible = qsPanelVisible &&  (expanded || !keyguardShowing || mHeaderAnimating
                 || mShowCollapsedOnKeyguard);
         mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
         mQSFooterActionController.setVisible(footerVisible);
         mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (expanded && !mStackScrollerOverscrolling));
-        mQSPanelController.setVisibility(
-                !mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
+        mQSPanelController.setVisibility(qsPanelVisible ? View.VISIBLE : View.INVISIBLE);
+        if (DEBUG) {
+            Log.d(TAG, "Footer: " + footerVisible + ", QS Panel: " + qsPanelVisible);
+        }
     }
 
     private boolean isKeyguardState() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index a70f534..e088f54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -36,6 +36,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -191,10 +192,16 @@
                     mContext,
                     ROUTE_TYPE_REMOTE_DISPLAY,
                     v -> {
-                        mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
-                        holder.mDialog.dismiss();
+                        ActivityLaunchAnimator.Controller controller =
+                                mDialogLaunchAnimator.createActivityLaunchController(v);
+
+                        if (controller == null) {
+                            holder.mDialog.dismiss();
+                        }
+
                         mActivityStarter
-                                .postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
+                                .postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
+                                        controller);
                     });
             holder.init(dialog);
             SystemUIDialog.setShowForAllUsers(dialog, true);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 4fe155c..e1d2070 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -164,7 +164,7 @@
             if (connectedState != WifiEntry.CONNECTED_STATE_DISCONNECTED) {
                 mWifiListLayout.setOnClickListener(
                         v -> mInternetDialogController.launchWifiNetworkDetailsSetting(
-                                wifiEntry.getKey()));
+                                wifiEntry.getKey(), v));
                 return;
             }
             mWifiListLayout.setOnClickListener(v -> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 8b6ddb4..d1c7844 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -355,8 +355,8 @@
                                 isChecked, false);
                     }
                 });
-        mConnectedWifListLayout.setOnClickListener(v -> onClickConnectedWifi());
-        mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
+        mConnectedWifListLayout.setOnClickListener(this::onClickConnectedWifi);
+        mSeeAllLayout.setOnClickListener(this::onClickSeeMoreButton);
         mWiFiToggle.setOnCheckedChangeListener(
                 (buttonView, isChecked) -> {
                     if (mWifiManager == null) return;
@@ -519,7 +519,7 @@
         if (TextUtils.isEmpty(mWifiScanNotifyText.getText())) {
             final AnnotationLinkSpan.LinkInfo linkInfo = new AnnotationLinkSpan.LinkInfo(
                     AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
-                    v -> mInternetDialogController.launchWifiScanningSetting());
+                    mInternetDialogController::launchWifiScanningSetting);
             mWifiScanNotifyText.setText(AnnotationLinkSpan.linkify(
                     getContext().getText(R.string.wifi_scan_notify_message), linkInfo));
             mWifiScanNotifyText.setMovementMethod(LinkMovementMethod.getInstance());
@@ -527,15 +527,16 @@
         mWifiScanNotifyLayout.setVisibility(View.VISIBLE);
     }
 
-    void onClickConnectedWifi() {
+    void onClickConnectedWifi(View view) {
         if (mConnectedWifiEntry == null) {
             return;
         }
-        mInternetDialogController.launchWifiNetworkDetailsSetting(mConnectedWifiEntry.getKey());
+        mInternetDialogController.launchWifiNetworkDetailsSetting(mConnectedWifiEntry.getKey(),
+                view);
     }
 
-    void onClickSeeMoreButton() {
-        mInternetDialogController.launchNetworkSetting();
+    void onClickSeeMoreButton(View view) {
+        mInternetDialogController.launchNetworkSetting(view);
     }
 
     CharSequence getDialogTitleText() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index f89b7a3..b3bc3be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -72,6 +72,7 @@
 import com.android.settingslib.net.SignalStrengthUtil;
 import com.android.settingslib.wifi.WifiUtils;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -620,36 +621,32 @@
         return summary;
     }
 
-    void launchNetworkSetting() {
-        // Dismissing a dialog into its touch surface and starting an activity at the same time
-        // looks bad, so let's make sure the dialog just fades out quickly.
-        mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
-        mCallback.dismissDialog();
+    private void startActivity(Intent intent, View view) {
+        ActivityLaunchAnimator.Controller controller =
+                mDialogLaunchAnimator.createActivityLaunchController(view);
 
-        mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0);
+        if (controller == null) {
+            mCallback.dismissDialog();
+        }
+
+        mActivityStarter.postStartActivityDismissingKeyguard(intent, 0, controller);
     }
 
-    void launchWifiNetworkDetailsSetting(String key) {
+    void launchNetworkSetting(View view) {
+        startActivity(getSettingsIntent(), view);
+    }
+
+    void launchWifiNetworkDetailsSetting(String key, View view) {
         Intent intent = getWifiDetailsSettingsIntent(key);
         if (intent != null) {
-            // Dismissing a dialog into its touch surface and starting an activity at the same time
-            // looks bad, so let's make sure the dialog just fades out quickly.
-            mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
-            mCallback.dismissDialog();
-
-            mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+            startActivity(intent, view);
         }
     }
 
-    void launchWifiScanningSetting() {
-        // Dismissing a dialog into its touch surface and starting an activity at the same time
-        // looks bad, so let's make sure the dialog just fades out quickly.
-        mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
-        mCallback.dismissDialog();
-
+    void launchWifiScanningSetting(View view) {
         final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+        startActivity(intent, view);
     }
 
     void connectCarrierNetwork() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 8c8c5c8..88aa734 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -19,6 +19,7 @@
 import android.app.Dialog
 import android.content.Context
 import android.content.DialogInterface
+import android.content.DialogInterface.BUTTON_NEUTRAL
 import android.content.Intent
 import android.provider.Settings
 import android.view.LayoutInflater
@@ -84,16 +85,20 @@
             setPositiveButton(R.string.quick_settings_done) { _, _ ->
                 uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE)
             }
-            setNeutralButton(R.string.quick_settings_more_user_settings) { _, _ ->
+            setNeutralButton(R.string.quick_settings_more_user_settings, { _, _ ->
                 if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                    dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
                     uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
+                    val controller = dialogLaunchAnimator.createActivityLaunchController(
+                        getButton(BUTTON_NEUTRAL))
+
+                    if (controller == null) {
+                        dismiss()
+                    }
+
                     activityStarter.postStartActivityDismissingKeyguard(
-                        USER_SETTINGS_INTENT,
-                        0
-                    )
+                        USER_SETTINGS_INTENT, 0, controller)
                 }
-            }
+            }, false /* dismissOnClick */)
             val gridFrame = LayoutInflater.from(this.context)
                 .inflate(R.layout.qs_user_dialog_content, null)
             setView(gridFrame)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index b312ce2..8366bdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -67,7 +67,7 @@
     wakefulnessLifecycle: WakefulnessLifecycle,
     configurationController: ConfigurationController,
     falsingManager: FalsingManager,
-    dumpManager: DumpManager,
+    dumpManager: DumpManager
 ) : Dumpable {
     private var pulseHeight: Float = 0f
     private var useSplitShade: Boolean = false
@@ -363,6 +363,7 @@
         notificationPanelController.setKeyguardOnlyContentAlpha(1.0f - scrimProgress)
         depthController.transitionToFullShadeProgress = scrimProgress
         udfpsKeyguardViewController?.setTransitionToFullShadeProgress(scrimProgress)
+        statusbar.setTransitionToFullShadeProgress(scrimProgress)
     }
 
     private fun setDragDownAmountAnimated(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index fcbe179..02870a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -32,6 +32,7 @@
 import android.text.format.DateFormat;
 import android.util.FloatProperty;
 import android.util.Log;
+import android.view.Choreographer;
 import android.view.InsetsFlags;
 import android.view.InsetsVisibilities;
 import android.view.View;
@@ -44,6 +45,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.jank.InteractionJankMonitor.Configuration;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
@@ -352,8 +354,17 @@
     }
 
     private void beginInteractionJankMonitor() {
+        final boolean shouldPost =
+                (mIsDozing && mDozeAmount == 0) || (!mIsDozing && mDozeAmount == 1);
         if (mInteractionJankMonitor != null && mView != null && mView.isAttachedToWindow()) {
-            mInteractionJankMonitor.begin(mView, getCujType());
+            if (shouldPost) {
+                Choreographer.getInstance().postCallback(
+                        Choreographer.CALLBACK_ANIMATION, this::beginInteractionJankMonitor, null);
+            } else {
+                Configuration.Builder builder = Configuration.Builder.withView(getCujType(), mView)
+                        .setDeferMonitorForAnimationStart(false);
+                mInteractionJankMonitor.begin(builder);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 5361a671..2b924a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -34,6 +34,7 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.wifi.WifiStatusTracker;
 import com.android.systemui.R;
+import com.android.systemui.util.Assert;
 
 import java.io.PrintWriter;
 
@@ -190,6 +191,7 @@
     }
 
     private void handleStatusUpdated() {
+        Assert.isMainThread();
         copyWifiStates();
         notifyListenersIfNecessary();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index d93c013..a0f8d05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -135,9 +135,6 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.idle.IdleHostView;
-import com.android.systemui.idle.IdleHostViewController;
-import com.android.systemui.idle.dagger.IdleViewComponent;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.media.MediaDataManager;
@@ -322,7 +319,6 @@
     private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
     private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
     private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
-    private final IdleViewComponent.Factory mIdleViewComponentFactory;
     private final FragmentService mFragmentService;
     private final ScrimController mScrimController;
     private final PrivacyDotViewController mPrivacyDotViewController;
@@ -356,8 +352,6 @@
     @Nullable
     private CommunalHostViewController mCommunalViewController;
     private KeyguardStatusViewController mKeyguardStatusViewController;
-    @Nullable
-    private IdleHostViewController mIdleHostViewController;
     private LockIconViewController mLockIconViewController;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
     private NotificationsQSContainerController mNotificationsQSContainerController;
@@ -369,7 +363,6 @@
     private VelocityTracker mQsVelocityTracker;
     private boolean mQsTracking;
 
-    private IdleHostView mIdleHostView;
     private CommunalHostView mCommunalView;
 
     /**
@@ -768,7 +761,6 @@
             KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
             KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
             CommunalViewComponent.Factory communalViewComponentFactory,
-            IdleViewComponent.Factory idleViewComponentFactory,
             LockscreenShadeTransitionController lockscreenShadeTransitionController,
             NotificationGroupManagerLegacy groupManager,
             NotificationIconAreaController notificationIconAreaController,
@@ -839,7 +831,6 @@
         mCommunalViewComponentFactory = communalViewComponentFactory;
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
-        mIdleViewComponentFactory = idleViewComponentFactory;
         mDepthController = notificationShadeDepthController;
         mContentResolver = contentResolver;
         mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
@@ -966,7 +957,6 @@
     private void onFinishInflate() {
         loadDimens();
         mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
-        mIdleHostView = mView.findViewById(R.id.idle_host_view);
         mCommunalView = mView.findViewById(R.id.communal_host);
 
         FrameLayout userAvatarContainer = null;
@@ -989,12 +979,6 @@
                         .getKeyguardStatusBarViewController();
         mKeyguardStatusBarViewController.init();
 
-        if (mIdleHostView != null) {
-            IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(mIdleHostView);
-            mIdleHostViewController = idleViewComponent.getIdleHostViewController();
-            mIdleHostViewController.init();
-        }
-
         if (mCommunalView != null) {
             CommunalViewComponent communalViewComponent =
                     mCommunalViewComponentFactory.build(mCommunalView);
@@ -1008,7 +992,6 @@
                 mView.findViewById(R.id.keyguard_status_view),
                 userAvatarContainer,
                 keyguardUserSwitcherView,
-                mIdleHostView,
                 mCommunalView);
 
         NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
@@ -1101,7 +1084,6 @@
     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
             FrameLayout userAvatarView,
             KeyguardUserSwitcherView keyguardUserSwitcherView,
-            IdleHostView idleHostView,
             CommunalHostView communalView) {
         // Re-associate the KeyguardStatusViewController
         KeyguardStatusViewComponent statusViewComponent =
@@ -1109,13 +1091,6 @@
         mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
         mKeyguardStatusViewController.init();
 
-        if (idleHostView != null && idleHostView != mIdleHostView) {
-            mIdleHostView = idleHostView;
-            IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(idleHostView);
-            mIdleHostViewController = idleViewComponent.getIdleHostViewController();
-            mIdleHostViewController.init();
-        }
-
         if (mKeyguardUserSwitcherController != null) {
             // Try to close the switcher so that callbacks are triggered if necessary.
             // Otherwise, NPV can get into a state where some of the views are still hidden
@@ -1287,7 +1262,7 @@
                         showKeyguardUserSwitcher /* enabled */);
 
         updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
-                keyguardUserSwitcherView, mView.findViewById(R.id.idle_host_view), mCommunalView);
+                keyguardUserSwitcherView, mCommunalView);
 
         // Update keyguard bottom area
         int index = mView.indexOfChild(mKeyguardBottomArea);
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 8e4feb8..82e0e67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -344,6 +344,7 @@
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     private final DreamOverlayStateController mDreamOverlayStateController;
     private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
+    private float mTransitionToFullShadeProgress = 0f;
 
     void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
         updateBubblesVisibility();
@@ -2583,9 +2584,9 @@
             return controllerFromStatusBar.get();
         }
 
-        if (dismissShade && rootView == mNotificationShadeWindowView) {
-            // We are animating a view in the shade. We have to make sure that we collapse it when
-            // the animation ends or is cancelled.
+        if (dismissShade) {
+            // If the view is not in the status bar, then we are animating a view in the shade.
+            // We have to make sure that we collapse it when the animation ends or is cancelled.
             return new StatusBarLaunchAnimatorController(animationController, this,
                     true /* isLaunchForActivity */);
         }
@@ -3777,6 +3778,15 @@
         updateScrimController();
     }
 
+    /**
+     * Set the amount of progress we are currently in if we're transitioning to the full shade.
+     * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+     * shade.
+     */
+    public void setTransitionToFullShadeProgress(float transitionToFullShadeProgress) {
+        mTransitionToFullShadeProgress = transitionToFullShadeProgress;
+    }
+
     @VisibleForTesting
     public void updateScrimController() {
         Trace.beginSection("StatusBar#updateScrimController");
@@ -3795,7 +3805,8 @@
         mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
 
         if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
-            if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED) {
+            if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+                    || mTransitionToFullShadeProgress > 0f) {
                 mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
             } else {
                 mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index d42a423..11d9c31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -68,10 +68,13 @@
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -87,7 +90,7 @@
 public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
         StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener,
         PanelExpansionListener, NavigationModeController.ModeChangedListener,
-        KeyguardViewController {
+        KeyguardViewController, FoldAodAnimationController.FoldAodAnimationStatus {
 
     // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
     private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3;
@@ -113,6 +116,8 @@
     private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
     private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
     private final DreamOverlayStateController mDreamOverlayStateController;
+    @Nullable
+    private final FoldAodAnimationController mFoldAodAnimationController;
     private KeyguardMessageAreaController mKeyguardMessageAreaController;
     private final Lazy<ShadeController> mShadeController;
     private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@@ -186,6 +191,7 @@
     private boolean mPulsing;
     private boolean mGesturalNav;
     private boolean mIsDocked;
+    private boolean mScreenOffAnimationPlaying;
 
     protected boolean mFirstUpdate = true;
     protected boolean mLastShowing;
@@ -199,6 +205,7 @@
     private boolean mLastIsDocked;
     private boolean mLastPulsing;
     private int mLastBiometricMode;
+    private boolean mLastScreenOffAnimationPlaying;
     private boolean mQsExpanded;
 
     private OnDismissAction mAfterKeyguardGoneAction;
@@ -246,6 +253,7 @@
             NotificationMediaManager notificationMediaManager,
             KeyguardBouncer.Factory keyguardBouncerFactory,
             KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
+            Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
             Lazy<ShadeController> shadeController,
             LatencyTracker latencyTracker) {
         mContext = context;
@@ -264,6 +272,8 @@
         mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
         mShadeController = shadeController;
         mLatencyTracker = latencyTracker;
+        mFoldAodAnimationController = sysUIUnfoldComponent
+                .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
     }
 
     @Override
@@ -317,6 +327,9 @@
         mConfigurationController.addCallback(this);
         mGesturalNav = QuickStepContract.isGesturalMode(
                 mNavigationModeController.addListener(this));
+        if (mFoldAodAnimationController != null) {
+            mFoldAodAnimationController.addCallback(this);
+        }
         if (mDockManager != null) {
             mDockManager.addListener(mDockEventListener);
             mIsDocked = mDockManager.isDocked();
@@ -965,6 +978,10 @@
     private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
         @Override
         public void run() {
+            NavigationBarView view = mStatusBar.getNavigationBarView();
+            if (view != null) {
+                view.setVisibility(View.VISIBLE);
+            }
             mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
                     .show(navigationBars());
         }
@@ -1019,6 +1036,7 @@
         mLastRemoteInputActive = remoteInputActive;
         mLastDozing = mDozing;
         mLastPulsing = mPulsing;
+        mLastScreenOffAnimationPlaying = mScreenOffAnimationPlaying;
         mLastBiometricMode = mBiometricUnlockController.getMode();
         mLastGesturalNav = mGesturalNav;
         mLastIsDocked = mIsDocked;
@@ -1054,14 +1072,15 @@
     /**
      * @return Whether the navigation bar should be made visible based on the current state.
      */
-    protected boolean isNavBarVisible() {
-        int biometricMode = mBiometricUnlockController.getMode();
+    public boolean isNavBarVisible() {
+        boolean isWakeAndUnlockPulsing = mBiometricUnlockController != null
+                && mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
         boolean keyguardShowing = mShowing && !mOccluded;
-        boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
+        boolean hideWhileDozing = mDozing && !isWakeAndUnlockPulsing;
         boolean keyguardWithGestureNav = (keyguardShowing && !mDozing || mPulsing && !mIsDocked)
                 && mGesturalNav;
-        return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
-                || mRemoteInputActive || keyguardWithGestureNav
+        return (!keyguardShowing && !hideWhileDozing && !mScreenOffAnimationPlaying
+                || mBouncer.isShowing() || mRemoteInputActive || keyguardWithGestureNav
                 || mGlobalActionsVisible);
     }
 
@@ -1073,8 +1092,8 @@
         boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
         boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing
                 || mLastPulsing && !mLastIsDocked) && mLastGesturalNav;
-        return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
-                || mLastRemoteInputActive || keyguardWithGestureNav
+        return (!keyguardShowing && !hideWhileDozing && !mLastScreenOffAnimationPlaying
+                || mLastBouncerShowing || mLastRemoteInputActive || keyguardWithGestureNav
                 || mLastGlobalActionsVisible);
     }
 
@@ -1224,6 +1243,13 @@
         setDozing(isDozing);
     }
 
+    @Override
+    public void onFoldToAodAnimationChanged() {
+        if (mFoldAodAnimationController != null) {
+            mScreenOffAnimationPlaying = mFoldAodAnimationController.shouldPlayAnimation();
+        }
+    }
+
     /**
      * Whether qs is currently expanded.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index 2ba37c2..09fca100 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.statusbar.phone
 
+import android.view.View
 import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.animation.LaunchAnimator
 
@@ -12,6 +13,11 @@
     private val statusBar: StatusBar,
     private val isLaunchForActivity: Boolean = true
 ) : ActivityLaunchAnimator.Controller by delegate {
+    // Always sync the opening window with the shade, given that we draw a hole punch in the shade
+    // of the same size and position as the opening app to make it visible.
+    override val openingWindowSyncView: View?
+        get() = statusBar.notificationShadeWindowView
+
     override fun onIntentStarted(willAnimate: Boolean) {
         delegate.onIntentStarted(willAnimate)
         if (!willAnimate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 6e1ec9c..9722528 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -44,6 +44,9 @@
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Base class for dialogs that should appear over panels and keyguard.
  *
@@ -68,6 +71,8 @@
     private int mLastConfigurationWidthDp = -1;
     private int mLastConfigurationHeightDp = -1;
 
+    private List<Runnable> mOnCreateRunnables = new ArrayList<>();
+
     public SystemUIDialog(Context context) {
         this(context, R.style.Theme_SystemUI_Dialog);
     }
@@ -110,6 +115,10 @@
         mLastConfigurationWidthDp = config.screenWidthDp;
         mLastConfigurationHeightDp = config.screenHeightDp;
         updateWindowSize();
+
+        for (int i = 0; i < mOnCreateRunnables.size(); i++) {
+            mOnCreateRunnables.get(i).run();
+        }
     }
 
     private void updateWindowSize() {
@@ -197,16 +206,67 @@
         setMessage(mContext.getString(resId));
     }
 
+    /**
+     * Set a listener to be invoked when the positive button of the dialog is pressed. The dialog
+     * will automatically be dismissed when the button is clicked.
+     */
     public void setPositiveButton(int resId, OnClickListener onClick) {
-        setButton(BUTTON_POSITIVE, mContext.getString(resId), onClick);
+        setPositiveButton(resId, onClick, true /* dismissOnClick */);
     }
 
+    /**
+     * Set a listener to be invoked when the positive button of the dialog is pressed. The dialog
+     * will be dismissed when the button is clicked iff {@code dismissOnClick} is true.
+     */
+    public void setPositiveButton(int resId, OnClickListener onClick, boolean dismissOnClick) {
+        setButton(BUTTON_POSITIVE, resId, onClick, dismissOnClick);
+    }
+
+    /**
+     * Set a listener to be invoked when the negative button of the dialog is pressed. The dialog
+     * will automatically be dismissed when the button is clicked.
+     */
     public void setNegativeButton(int resId, OnClickListener onClick) {
-        setButton(BUTTON_NEGATIVE, mContext.getString(resId), onClick);
+        setNegativeButton(resId, onClick, true /* dismissOnClick */);
     }
 
+    /**
+     * Set a listener to be invoked when the negative button of the dialog is pressed. The dialog
+     * will be dismissed when the button is clicked iff {@code dismissOnClick} is true.
+     */
+    public void setNegativeButton(int resId, OnClickListener onClick, boolean dismissOnClick) {
+        setButton(BUTTON_NEGATIVE, resId, onClick, dismissOnClick);
+    }
+
+    /**
+     * Set a listener to be invoked when the neutral button of the dialog is pressed. The dialog
+     * will automatically be dismissed when the button is clicked.
+     */
     public void setNeutralButton(int resId, OnClickListener onClick) {
-        setButton(BUTTON_NEUTRAL, mContext.getString(resId), onClick);
+        setNeutralButton(resId, onClick, true /* dismissOnClick */);
+    }
+
+    /**
+     * Set a listener to be invoked when the neutral button of the dialog is pressed. The dialog
+     * will be dismissed when the button is clicked iff {@code dismissOnClick} is true.
+     */
+    public void setNeutralButton(int resId, OnClickListener onClick, boolean dismissOnClick) {
+        setButton(BUTTON_NEUTRAL, resId, onClick, dismissOnClick);
+    }
+
+    private void setButton(int whichButton, int resId, OnClickListener onClick,
+            boolean dismissOnClick) {
+        if (dismissOnClick) {
+            setButton(whichButton, mContext.getString(resId), onClick);
+        } else {
+            // Set a null OnClickListener to make sure the button is still created and shown.
+            setButton(whichButton, mContext.getString(resId), (OnClickListener) null);
+
+            // When the dialog is created, set the click listener but don't dismiss the dialog when
+            // it is clicked.
+            mOnCreateRunnables.add(() -> getButton(whichButton).setOnClickListener(
+                    view -> onClick.onClick(this, whichButton)));
+        }
     }
 
     public static void setShowForAllUsers(Dialog dialog, boolean show) {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 2e627a8..2a9076e 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -48,17 +48,17 @@
     private var pendingScrimReadyCallback: Runnable? = null
 
     private var shouldPlayAnimation = false
+    private var isAnimationPlaying = false
+
     private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
 
     private val startAnimationRunnable = Runnable {
         statusBar.notificationPanelViewController.startFoldToAodAnimation {
             // End action
-            isAnimationPlaying = false
+            setAnimationState(playing = false)
         }
     }
 
-    private var isAnimationPlaying = false
-
     override fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
         this.statusBar = statusBar
 
@@ -71,17 +71,13 @@
     override fun startAnimation(): Boolean =
         if (alwaysOnEnabled &&
             wakefulnessLifecycle.lastSleepReason == PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD &&
-            globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0") {
-            shouldPlayAnimation = true
-
-            isAnimationPlaying = true
+            globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0"
+        ) {
+            setAnimationState(playing = true)
             statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
-
-            statusListeners.forEach(FoldAodAnimationStatus::onFoldToAodAnimationChanged)
-
             true
         } else {
-            shouldPlayAnimation = false
+            setAnimationState(playing = false)
             false
         }
 
@@ -91,8 +87,13 @@
             statusBar.notificationPanelViewController.cancelFoldToAodAnimation();
         }
 
-        shouldPlayAnimation = false
-        isAnimationPlaying = false
+        setAnimationState(playing = false)
+    }
+
+    private fun setAnimationState(playing: Boolean) {
+        shouldPlayAnimation = playing
+        isAnimationPlaying = playing
+        statusListeners.forEach(FoldAodAnimationStatus::onFoldToAodAnimationChanged)
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 589eeb5..d5df9fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -46,7 +46,7 @@
 @RunWithLooper
 class ActivityLaunchAnimatorTest : SysuiTestCase() {
     private val launchContainer = LinearLayout(mContext)
-    private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+    private val testLaunchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
     @Mock lateinit var callback: ActivityLaunchAnimator.Callback
     @Mock lateinit var listener: ActivityLaunchAnimator.Listener
     @Spy private val controller = TestLaunchAnimatorController(launchContainer)
@@ -58,7 +58,7 @@
 
     @Before
     fun setup() {
-        activityLaunchAnimator = ActivityLaunchAnimator(launchAnimator)
+        activityLaunchAnimator = ActivityLaunchAnimator(testLaunchAnimator, testLaunchAnimator)
         activityLaunchAnimator.callback = callback
         activityLaunchAnimator.addListener(listener)
     }
@@ -129,13 +129,11 @@
     @Test
     fun animatesIfActivityIsAlreadyOpenAndIsOnKeyguard() {
         `when`(callback.isOnKeyguard()).thenReturn(true)
-        val animator = ActivityLaunchAnimator(launchAnimator)
-        animator.callback = callback
 
         val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
         var animationAdapter: RemoteAnimationAdapter? = null
 
-        startIntentWithAnimation(animator) { adapter ->
+        startIntentWithAnimation(activityLaunchAnimator) { adapter ->
             animationAdapter = adapter
             ActivityManager.START_DELIVERED_TO_TOP
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 61e78f5..fe2efa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -20,8 +20,10 @@
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertNotNull
+import junit.framework.Assert.assertNull
 import junit.framework.Assert.assertTrue
 import org.junit.After
+import org.junit.Assert.assertNotEquals
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -43,7 +45,7 @@
     @Before
     fun setUp() {
         dialogLaunchAnimator = DialogLaunchAnimator(
-            dreamManager, launchAnimator, forceDisableSynchronization = true)
+            dreamManager, launchAnimator, isForTesting = true)
     }
 
     @After
@@ -92,11 +94,6 @@
 
         // Clicking the transparent background should dismiss the dialog.
         runOnMainThreadAndWaitForIdleSync {
-            // TODO(b/204561691): Remove this call to disableAllCurrentDialogsExitAnimations() and
-            // make sure that the test still pass on git_master/cf_x86_64_phone-userdebug in
-            // Forrest.
-            dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
-
             transparentBackground.performClick()
         }
         assertFalse(dialog.isShowing)
@@ -110,7 +107,6 @@
         assertTrue(firstDialog.isShowing)
         assertTrue(secondDialog.isShowing)
         runOnMainThreadAndWaitForIdleSync {
-            dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
             dialogLaunchAnimator.dismissStack(secondDialog)
         }
 
@@ -118,7 +114,63 @@
         assertFalse(secondDialog.isShowing)
     }
 
+    @Test
+    fun testActivityLaunchControllerFromDialog() {
+        val firstDialog = createAndShowDialog()
+        val secondDialog = createDialogAndShowFromDialog(firstDialog)
+
+        val controller =
+            dialogLaunchAnimator.createActivityLaunchController(secondDialog.contentView)!!
+
+        // The dialog shouldn't be dismissable during the animation.
+        runOnMainThreadAndWaitForIdleSync {
+            controller.onLaunchAnimationStart(isExpandingFullyAbove = true)
+            secondDialog.dismiss()
+        }
+        assertTrue(secondDialog.isShowing)
+
+        // Both dialogs should be dismissed at the end of the animation.
+        runOnMainThreadAndWaitForIdleSync {
+            controller.onLaunchAnimationEnd(isExpandingFullyAbove = true)
+        }
+        assertFalse(firstDialog.isShowing)
+        assertFalse(secondDialog.isShowing)
+    }
+
+    @Test
+    fun testActivityLaunchFromHiddenDialog() {
+        val dialog = createAndShowDialog()
+        runOnMainThreadAndWaitForIdleSync {
+            dialog.hide()
+        }
+        assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+    }
+
+    @Test
+    fun testDialogAnimationIsChangedByAnimator() {
+        // Important: the power menu animation relies on this behavior to know when to animate (see
+        // http://ag/16774605).
+        val dialog = runOnMainThreadAndWaitForIdleSync { TestDialog(context) }
+        dialog.window.setWindowAnimations(0)
+        assertEquals(0, dialog.window.attributes.windowAnimations)
+
+        val touchSurface = createTouchSurface()
+        runOnMainThreadAndWaitForIdleSync {
+            dialogLaunchAnimator.showFromView(dialog, touchSurface)
+        }
+        assertNotEquals(0, dialog.window.attributes.windowAnimations)
+    }
+
     private fun createAndShowDialog(): TestDialog {
+        val touchSurface = createTouchSurface()
+        return runOnMainThreadAndWaitForIdleSync {
+            val dialog = TestDialog(context)
+            dialogLaunchAnimator.showFromView(dialog, touchSurface)
+            dialog
+        }
+    }
+
+    private fun createTouchSurface(): View {
         return runOnMainThreadAndWaitForIdleSync {
             val touchSurfaceRoot = LinearLayout(context)
             val touchSurface = View(context)
@@ -129,9 +181,7 @@
             ViewUtils.attachView(touchSurfaceRoot)
             attachedViews.add(touchSurfaceRoot)
 
-            val dialog = TestDialog(context)
-            dialogLaunchAnimator.showFromView(dialog, touchSurface)
-            dialog
+            touchSurface
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
deleted file mode 100644
index 500205c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.communal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.wifi.WifiInfo;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.conditions.CommunalTrustedNetworkCondition;
-import com.android.systemui.util.settings.FakeSettings;
-import com.android.systemui.utils.os.FakeHandler;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class CommunalTrustedNetworkConditionTest extends SysuiTestCase {
-    @Mock private ConnectivityManager mConnectivityManager;
-
-    @Captor private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor;
-
-    private final Handler mHandler = new FakeHandler(Looper.getMainLooper());
-    private CommunalTrustedNetworkCondition mCondition;
-
-    private final String mTrustedWifi1 = "wifi-1";
-    private final String mTrustedWifi2 = "wifi-2";
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        final FakeSettings secureSettings = new FakeSettings();
-        secureSettings.putStringForUser(Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS,
-                mTrustedWifi1 + CommunalTrustedNetworkCondition.SETTINGS_STRING_DELIMINATOR
-                        + mTrustedWifi2, UserHandle.USER_SYSTEM);
-        mCondition = new CommunalTrustedNetworkCondition(mHandler, mConnectivityManager,
-                secureSettings);
-    }
-
-    @Test
-    public void updateCallback_connectedToTrustedNetwork_reportsTrue() {
-        final CommunalTrustedNetworkCondition.Callback callback =
-                mock(CommunalTrustedNetworkCondition.Callback.class);
-        mCondition.addCallback(callback);
-
-        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
-
-        // Connected to trusted Wi-Fi network.
-        final Network network = mock(Network.class);
-        networkCallback.onAvailable(network);
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
-
-        // Verifies that the callback is triggered.
-        verify(callback).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionMet()).isTrue();
-    }
-
-    @Test
-    public void updateCallback_switchedToAnotherTrustedNetwork_reportsNothing() {
-        final CommunalTrustedNetworkCondition.Callback callback =
-                mock(CommunalTrustedNetworkCondition.Callback.class);
-        mCondition.addCallback(callback);
-
-        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
-
-        // Connected to a trusted Wi-Fi network.
-        final Network network = mock(Network.class);
-        networkCallback.onAvailable(network);
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
-        clearInvocations(callback);
-
-        // Connected to another trusted Wi-Fi network.
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi2));
-
-        // Verifies that the callback is not triggered.
-        verify(callback, never()).onConditionChanged(eq(mCondition));
-    }
-
-    @Test
-    public void updateCallback_connectedToNonTrustedNetwork_reportsFalse() {
-        final CommunalTrustedNetworkCondition.Callback callback =
-                mock(CommunalTrustedNetworkCondition.Callback.class);
-        mCondition.addCallback(callback);
-
-        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
-
-        // Connected to trusted Wi-Fi network.
-        final Network network = mock(Network.class);
-        networkCallback.onAvailable(network);
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
-
-        Mockito.clearInvocations(callback);
-        // Connected to non-trusted Wi-Fi network.
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities("random-wifi"));
-
-        // Verifies that the callback is triggered.
-        verify(callback).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionMet()).isFalse();
-    }
-
-    @Test
-    public void updateCallback_disconnectedFromNetwork_reportsFalse() {
-        final CommunalTrustedNetworkCondition.Callback callback =
-                mock(CommunalTrustedNetworkCondition.Callback.class);
-        mCondition.addCallback(callback);
-
-        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
-
-        // Connected to Wi-Fi.
-        final Network network = mock(Network.class);
-        networkCallback.onAvailable(network);
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
-        clearInvocations(callback);
-
-        // Disconnected from Wi-Fi.
-        networkCallback.onLost(network);
-
-        // Verifies that the callback is triggered.
-        verify(callback).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionMet()).isFalse();
-    }
-
-    // Captures and returns the network callback, assuming it is registered with the connectivity
-    // manager.
-    private ConnectivityManager.NetworkCallback captureNetworkCallback() {
-        verify(mConnectivityManager).registerNetworkCallback(any(NetworkRequest.class),
-                mNetworkCallbackCaptor.capture());
-        return mNetworkCallbackCaptor.getValue();
-    }
-
-    private NetworkCapabilities fakeNetworkCapabilities(String ssid) {
-        final NetworkCapabilities networkCapabilities = mock(NetworkCapabilities.class);
-        final WifiInfo wifiInfo = mock(WifiInfo.class);
-        when(wifiInfo.getSSID()).thenReturn(ssid);
-        when(networkCapabilities.getTransportInfo()).thenReturn(wifiInfo);
-        return networkCapabilities;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
index 47ab17d..d3c465d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
@@ -23,22 +23,24 @@
 import android.graphics.drawable.Icon
 import android.service.controls.Control
 import android.service.controls.DeviceTypes
+import android.service.controls.templates.ControlTemplate
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
+import android.view.View
 import android.view.ViewGroup
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
-import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.util.time.FakeSystemClock
-import org.junit.runner.RunWith
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.ControlsMetricsLogger
 import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
 
 @SmallTest
@@ -49,11 +51,12 @@
     private val clock = FakeSystemClock()
 
     private lateinit var cvh: ControlViewHolder
+    private lateinit var baseLayout: ViewGroup
 
     @Before
     fun setUp() {
         TestableLooper.get(this).runWithLooper {
-            val baseLayout = LayoutInflater.from(mContext).inflate(
+            baseLayout = LayoutInflater.from(mContext).inflate(
                     R.layout.controls_base_item, null, false) as ViewGroup
 
             cvh = ControlViewHolder(
@@ -106,6 +109,25 @@
 
         assertThat(cvh.icon.imageTintList).isEqualTo(customIconTintList)
     }
+
+    @Test
+    fun chevronIcon() {
+        val control = Control.StatefulBuilder(CONTROL_ID, mock(PendingIntent::class.java))
+            .setStatus(Control.STATUS_OK)
+            .setControlTemplate(ControlTemplate.NO_TEMPLATE)
+            .build()
+        val cws = ControlWithState(
+            ComponentName.createRelative("pkg", "cls"),
+            ControlInfo(
+                CONTROL_ID, CONTROL_TITLE, "subtitle", DeviceTypes.TYPE_AIR_FRESHENER
+            ),
+            control
+        )
+        cvh.bindData(cws, false)
+        val chevronIcon = baseLayout.findViewById<View>(R.id.chevron_icon)
+
+        assertThat(chevronIcon.visibility).isEqualTo(View.VISIBLE)
+    }
 }
 
 private const val CONTROL_ID = "CONTROL_ID"
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 8078b6c..5684429 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -42,12 +42,14 @@
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.testing.AndroidTestingRunner;
 import android.testing.UiThreadTest;
+import android.view.Display;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
@@ -444,4 +446,20 @@
 
         assertTrue(mServiceFake.requestedWakeup);
     }
+
+    @Test
+    public void testDozePulsing_displayRequiresBlanking_screenState() {
+        DozeParameters dozeParameters = mock(DozeParameters.class);
+        when(dozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
+
+        assertEquals(Display.STATE_OFF, DOZE_REQUEST_PULSE.screenState(dozeParameters));
+    }
+
+    @Test
+    public void testDozePulsing_displayDoesNotRequireBlanking_screenState() {
+        DozeParameters dozeParameters = mock(DozeParameters.class);
+        when(dozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+
+        assertEquals(Display.STATE_ON, DOZE_REQUEST_PULSE.screenState(dozeParameters));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt
deleted file mode 100644
index 98b9252..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.idle
-
-import android.hardware.Sensor
-import android.hardware.SensorEventListener
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.sensors.AsyncSensorManager
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.any
-import org.mockito.Mockito.eq
-import org.mockito.Mockito.never
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class AmbientLightModeMonitorTest : SysuiTestCase() {
-    @Mock private lateinit var sensorManager: AsyncSensorManager
-    @Mock private lateinit var sensor: Sensor
-    @Mock private lateinit var algorithm: AmbientLightModeMonitor.DebounceAlgorithm
-
-    private lateinit var ambientLightModeMonitor: AmbientLightModeMonitor
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        `when`(sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(sensor)
-
-        ambientLightModeMonitor = AmbientLightModeMonitor(algorithm, sensorManager)
-    }
-
-    @Test
-    fun shouldRegisterSensorEventListenerOnStart() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        ambientLightModeMonitor.start(callback)
-
-        verify(sensorManager).registerListener(any(), eq(sensor), anyInt())
-    }
-
-    @Test
-    fun shouldUnregisterSensorEventListenerOnStop() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        ambientLightModeMonitor.start(callback)
-
-        val sensorEventListener = captureSensorEventListener()
-
-        ambientLightModeMonitor.stop()
-
-        verify(sensorManager).unregisterListener(eq(sensorEventListener))
-    }
-
-    @Test
-    fun shouldStartDebounceAlgorithmOnStart() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        ambientLightModeMonitor.start(callback)
-
-        verify(algorithm).start(eq(callback))
-    }
-
-    @Test
-    fun shouldStopDebounceAlgorithmOnStop() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        ambientLightModeMonitor.start(callback)
-        ambientLightModeMonitor.stop()
-
-        verify(algorithm).stop()
-    }
-
-    @Test
-    fun shouldNotRegisterForSensorUpdatesIfSensorNotAvailable() {
-        `when`(sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(null)
-        val ambientLightModeMonitor = AmbientLightModeMonitor(algorithm, sensorManager)
-
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        ambientLightModeMonitor.start(callback)
-
-        verify(sensorManager, never()).registerListener(any(), any(Sensor::class.java), anyInt())
-    }
-
-    // Captures [SensorEventListener], assuming it has been registered with [sensorManager].
-    private fun captureSensorEventListener(): SensorEventListener {
-        val captor = ArgumentCaptor.forClass(SensorEventListener::class.java)
-        verify(sensorManager).registerListener(captor.capture(), any(), anyInt())
-        return captor.value
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
deleted file mode 100644
index 3c24a3a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.idle;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-import android.os.PowerManager;
-import android.testing.AndroidTestingRunner;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import javax.inject.Provider;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class IdleHostViewControllerTest extends SysuiTestCase {
-    @Mock private BroadcastDispatcher mBroadcastDispatcher;
-    @Mock private PowerManager mPowerManager;
-    @Mock private IdleHostView mIdleHostView;
-    @Mock private Resources mResources;
-    @Mock private Provider<View> mViewProvider;
-    @Mock private KeyguardStateController mKeyguardStateController;
-    @Mock private StatusBarStateController mStatusBarStateController;
-    @Mock private AmbientLightModeMonitor mAmbientLightModeMonitor;
-
-    private KeyguardStateController.Callback mKeyguardStateCallback;
-    private StatusBarStateController.StateListener mStatusBarStateListener;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mResources.getBoolean(R.bool.config_enableIdleMode)).thenReturn(true);
-        when(mStatusBarStateController.isDozing()).thenReturn(false);
-
-        final IdleHostViewController controller = new IdleHostViewController(mBroadcastDispatcher,
-                mPowerManager, mIdleHostView, mResources, mViewProvider, mKeyguardStateController,
-                mStatusBarStateController, mAmbientLightModeMonitor);
-        controller.init();
-        controller.onViewAttached();
-
-        // Captures keyguard state controller callback.
-        ArgumentCaptor<KeyguardStateController.Callback> keyguardStateCallbackCaptor =
-                ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
-        verify(mKeyguardStateController).addCallback(keyguardStateCallbackCaptor.capture());
-        mKeyguardStateCallback = keyguardStateCallbackCaptor.getValue();
-
-        // Captures status bar state listener.
-        ArgumentCaptor<StatusBarStateController.StateListener> statusBarStateListenerCaptor =
-                ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
-        verify(mStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture());
-        mStatusBarStateListener = statusBarStateListenerCaptor.getValue();
-    }
-
-    @Test
-    public void testStartDozingWhenLowLight() {
-        // Keyguard showing.
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        mKeyguardStateCallback.onKeyguardShowingChanged();
-
-        // Regular ambient lighting.
-        final AmbientLightModeMonitor.Callback lightMonitorCallback =
-                captureAmbientLightModeMonitorCallback();
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-
-        // Verifies it doesn't go to sleep yet.
-        verify(mPowerManager, never()).goToSleep(anyLong(), anyInt(), anyInt());
-
-        // Ambient lighting becomes dim.
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-
-        // Verifies it goes to sleep.
-        verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
-    }
-
-    @Test
-    public void testWakeUpWhenRegularLight() {
-        // Keyguard showing.
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        mKeyguardStateCallback.onKeyguardShowingChanged();
-
-        // In low light / dozing.
-        final AmbientLightModeMonitor.Callback lightMonitorCallback =
-                captureAmbientLightModeMonitorCallback();
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-        mStatusBarStateListener.onDozingChanged(true /*isDozing*/);
-
-        // Regular ambient lighting.
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-
-        // Verifies it wakes up from sleep.
-        verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
-    }
-
-    // Captures [AmbientLightModeMonitor.Callback] assuming that the ambient light mode monitor
-    // has been started.
-    private AmbientLightModeMonitor.Callback captureAmbientLightModeMonitorCallback() {
-        ArgumentCaptor<AmbientLightModeMonitor.Callback> captor =
-                ArgumentCaptor.forClass(AmbientLightModeMonitor.Callback.class);
-        verify(mAmbientLightModeMonitor).start(captor.capture());
-        return captor.getValue();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/LightSensorDebounceAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/idle/LightSensorDebounceAlgorithmTest.kt
deleted file mode 100644
index ebc7345..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/idle/LightSensorDebounceAlgorithmTest.kt
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.idle
-
-import android.content.res.Resources
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.`when`
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class LightSensorDebounceAlgorithmTest : SysuiTestCase() {
-    @Mock private lateinit var resources: Resources
-
-    private val systemClock = FakeSystemClock()
-    private val executor = FakeExecutor(systemClock)
-
-    private lateinit var algorithm: LightSensorEventsDebounceAlgorithm
-
-    private val mockLightModeThreshold = 5
-    private val mockDarkModeThreshold = 2
-    private val mockLightModeSpan = 100
-    private val mockDarkModeSpan = 50
-    private val mockLightModeFrequency = 10
-    private val mockDarkModeFrequency = 5
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        `when`(resources.getInteger(R.integer.config_ambientLightModeThreshold))
-                .thenReturn(mockLightModeThreshold)
-        `when`(resources.getInteger(R.integer.config_ambientDarkModeThreshold))
-                .thenReturn(mockDarkModeThreshold)
-        `when`(resources.getInteger(R.integer.config_ambientLightModeSamplingSpanMillis))
-                .thenReturn(mockLightModeSpan)
-        `when`(resources.getInteger(R.integer.config_ambientDarkModeSamplingSpanMillis))
-                .thenReturn(mockDarkModeSpan)
-        `when`(resources.getInteger(R.integer.config_ambientLightModeSamplingFrequencyMillis))
-                .thenReturn(mockLightModeFrequency)
-        `when`(resources.getInteger(R.integer.config_ambientDarkModeSamplingFrequencyMillis))
-                .thenReturn(mockDarkModeFrequency)
-
-        algorithm = LightSensorEventsDebounceAlgorithm(executor, resources)
-    }
-
-    @Test
-    fun shouldOnlyTriggerCallbackWhenValueChanges() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        algorithm.start(callback)
-
-        // Light mode, should trigger callback.
-        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-        reset(callback)
-
-        // Light mode again, should NOT trigger callback.
-        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
-        verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-        reset(callback)
-
-        // Dark mode, should trigger callback.
-        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-        reset(callback)
-
-        // Dark mode again, should not trigger callback.
-        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
-        verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-    }
-
-    @Test
-    fun shouldReportUndecidedWhenNeitherLightNorDarkClaimIsTrue() {
-        algorithm.isDarkMode = false
-        algorithm.isLightMode = false
-
-        assertThat(algorithm.mode).isEqualTo(
-                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED)
-    }
-
-    @Test
-    fun shouldReportDarkModeAsLongAsDarkModeClaimIsTrue() {
-        algorithm.isDarkMode = true
-        algorithm.isLightMode = false
-
-        assertThat(algorithm.mode).isEqualTo(
-                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-
-        algorithm.isLightMode = true
-        assertThat(algorithm.mode).isEqualTo(
-                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-    }
-
-    @Test
-    fun shouldReportLightModeWhenLightModeClaimIsTrueAndDarkModeClaimIsFalse() {
-        algorithm.isLightMode = true
-        algorithm.isDarkMode = false
-
-        assertThat(algorithm.mode).isEqualTo(
-                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-    }
-
-    @Test
-    fun shouldSetIsLightModeToTrueWhenBundleAverageIsGreaterThanThreshold() {
-        // Note: [mockLightModeThreshold] is 5.0.
-        algorithm.bundleAverageLightMode = 5.1
-        assertThat(algorithm.isLightMode).isTrue()
-
-        algorithm.bundleAverageLightMode = 10.0
-        assertThat(algorithm.isLightMode).isTrue()
-
-        algorithm.bundleAverageLightMode = 20.0
-        assertThat(algorithm.isLightMode).isTrue()
-
-        algorithm.bundleAverageLightMode = 5.0
-        assertThat(algorithm.isLightMode).isFalse()
-
-        algorithm.bundleAverageLightMode = 3.0
-        assertThat(algorithm.isLightMode).isFalse()
-
-        algorithm.bundleAverageLightMode = 0.0
-        assertThat(algorithm.isLightMode).isFalse()
-    }
-
-    @Test
-    fun shouldSetIsDarkModeToTrueWhenBundleAverageIsLessThanThreshold() {
-        // Note: [mockDarkModeThreshold] is 2.0.
-        algorithm.bundleAverageDarkMode = 1.9
-        assertThat(algorithm.isDarkMode).isTrue()
-
-        algorithm.bundleAverageDarkMode = 1.0
-        assertThat(algorithm.isDarkMode).isTrue()
-
-        algorithm.bundleAverageDarkMode = 0.0
-        assertThat(algorithm.isDarkMode).isTrue()
-
-        algorithm.bundleAverageDarkMode = 2.0
-        assertThat(algorithm.isDarkMode).isFalse()
-
-        algorithm.bundleAverageDarkMode = 3.0
-        assertThat(algorithm.isDarkMode).isFalse()
-
-        algorithm.bundleAverageDarkMode = 10.0
-        assertThat(algorithm.isDarkMode).isFalse()
-    }
-
-    @Test
-    fun shouldCorrectlyCalculateAverageFromABundle() {
-        // For light mode.
-        algorithm.bundleLightMode = arrayListOf(1.0f, 3.0f, 5.0f, 7.0f)
-        assertThat(algorithm.bundleAverageLightMode).isEqualTo(4.0)
-
-        algorithm.bundleLightMode = arrayListOf(2.0f, 4.0f, 6.0f, 8.0f)
-        assertThat(algorithm.bundleAverageLightMode).isEqualTo(5.0)
-
-        // For dark mode.
-        algorithm.bundleDarkMode = arrayListOf(1.0f, 3.0f, 5.0f, 7.0f, 9.0f)
-        assertThat(algorithm.bundleAverageDarkMode).isEqualTo(5.0)
-
-        algorithm.bundleDarkMode = arrayListOf(2.0f, 4.0f, 6.0f, 8.0f, 10.0f)
-        assertThat(algorithm.bundleAverageDarkMode).isEqualTo(6.0)
-    }
-
-    @Test
-    fun shouldAddSensorEventUpdatesToBundles() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        // On start() one bundle is created for light and dark mode each.
-        algorithm.start(callback)
-        executor.runAllReady()
-
-        // Add 1 more bundle to queue for each mode.
-        algorithm.bundlesQueueLightMode.add(ArrayList())
-        algorithm.bundlesQueueDarkMode.add(ArrayList())
-
-        algorithm.onUpdateLightSensorEvent(1.0f)
-        algorithm.onUpdateLightSensorEvent(1.0f)
-        algorithm.onUpdateLightSensorEvent(2.0f)
-        algorithm.onUpdateLightSensorEvent(3.0f)
-        algorithm.onUpdateLightSensorEvent(4.0f)
-
-        val expectedValues = listOf(1.0f, 1.0f, 2.0f, 3.0f, 4.0f)
-
-        assertBundleContainsAll(algorithm.bundlesQueueLightMode[0], expectedValues)
-        assertBundleContainsAll(algorithm.bundlesQueueLightMode[1], expectedValues)
-        assertBundleContainsAll(algorithm.bundlesQueueDarkMode[0], expectedValues)
-        assertBundleContainsAll(algorithm.bundlesQueueDarkMode[1], expectedValues)
-    }
-
-    @Test
-    fun shouldCorrectlyEnqueueLightModeBundles() {
-        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(0)
-
-        algorithm.enqueueLightModeBundle.run()
-        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(1)
-
-        algorithm.enqueueLightModeBundle.run()
-        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(2)
-
-        algorithm.enqueueLightModeBundle.run()
-        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(3)
-
-        // Verifies dark mode bundles queue is not impacted.
-        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(0)
-    }
-
-    @Test
-    fun shouldCorrectlyEnqueueDarkModeBundles() {
-        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(0)
-
-        algorithm.enqueueDarkModeBundle.run()
-        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(1)
-
-        algorithm.enqueueDarkModeBundle.run()
-        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(2)
-
-        algorithm.enqueueDarkModeBundle.run()
-        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(3)
-
-        // Verifies light mode bundles queue is not impacted.
-        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(0)
-    }
-
-    @Test
-    fun shouldCorrectlyDequeueLightModeBundles() {
-        // Sets up the light mode bundles queue.
-        val bundle1 = arrayListOf(1.0f, 3.0f, 6.0f, 9.0f)
-        val bundle2 = arrayListOf(5.0f, 10f)
-        val bundle3 = arrayListOf(2.0f, 4.0f)
-        algorithm.bundlesQueueLightMode.add(bundle1)
-        algorithm.bundlesQueueLightMode.add(bundle2)
-        algorithm.bundlesQueueLightMode.add(bundle3)
-
-        // The committed bundle should be the first one in queue.
-        algorithm.dequeueLightModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleLightMode, bundle1)
-
-        algorithm.dequeueLightModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleLightMode, bundle2)
-
-        algorithm.dequeueLightModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleLightMode, bundle3)
-
-        // Verifies that the dark mode bundle is not impacted.
-        assertBundleContainsAll(algorithm.bundleDarkMode, listOf())
-    }
-
-    @Test
-    fun shouldCorrectlyDequeueDarkModeBundles() {
-        // Sets up the dark mode bundles queue.
-        val bundle1 = arrayListOf(2.0f, 4.0f)
-        val bundle2 = arrayListOf(5.0f, 10f)
-        val bundle3 = arrayListOf(1.0f, 3.0f, 6.0f, 9.0f)
-        algorithm.bundlesQueueDarkMode.add(bundle1)
-        algorithm.bundlesQueueDarkMode.add(bundle2)
-        algorithm.bundlesQueueDarkMode.add(bundle3)
-
-        // The committed bundle should be the first one in queue.
-        algorithm.dequeueDarkModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleDarkMode, bundle1)
-
-        algorithm.dequeueDarkModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleDarkMode, bundle2)
-
-        algorithm.dequeueDarkModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleDarkMode, bundle3)
-
-        // Verifies that the light mode bundle is not impacted.
-        assertBundleContainsAll(algorithm.bundleLightMode, listOf())
-    }
-
-    @Test
-    fun shouldSetLightSensorLevelFromSensorEventUpdates() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        algorithm.start(callback)
-
-        algorithm.onUpdateLightSensorEvent(1.0f)
-        assertThat(algorithm.lightSensorLevel).isEqualTo(1.0f)
-
-        algorithm.onUpdateLightSensorEvent(10.0f)
-        assertThat(algorithm.lightSensorLevel).isEqualTo(10.0f)
-
-        algorithm.onUpdateLightSensorEvent(0.0f)
-        assertThat(algorithm.lightSensorLevel).isEqualTo(0.0f)
-    }
-
-    @Test
-    fun shouldRippleFromSensorEventUpdatesDownToAmbientLightMode() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        algorithm.start(callback)
-        executor.runAllReady()
-
-        // Sensor event updates.
-        algorithm.onUpdateLightSensorEvent(10.0f)
-        algorithm.onUpdateLightSensorEvent(15.0f)
-        algorithm.onUpdateLightSensorEvent(12.0f)
-        algorithm.onUpdateLightSensorEvent(10.0f)
-
-        // Advances time so both light and dark claims have been calculated.
-        systemClock.advanceTime((mockLightModeSpan + 1).toLong())
-
-        // Verifies the callback is triggered the ambient mode has changed LIGHT.
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-    }
-
-    @Test
-    fun shouldRippleFromSensorEventUpdatesDownToAmbientDarkMode() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        algorithm.start(callback)
-        executor.runAllReady()
-
-        // Sensor event updates.
-        algorithm.onUpdateLightSensorEvent(1.0f)
-        algorithm.onUpdateLightSensorEvent(0.5f)
-        algorithm.onUpdateLightSensorEvent(1.2f)
-        algorithm.onUpdateLightSensorEvent(0.8f)
-
-        // Advances time so both light and dark claims have been calculated.
-        systemClock.advanceTime((mockLightModeSpan + 1).toLong())
-
-        // Verifies the callback is triggered the ambient mode has changed DARK.
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-    }
-
-    // Asserts that [bundle] contains the same elements as [expected], not necessarily in the same
-    // order.
-    private fun assertBundleContainsAll(bundle: ArrayList<Float>, expected: Collection<Float>) {
-        assertThat(bundle.size).isEqualTo(expected.size)
-        assertThat(bundle.containsAll(expected))
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 421ae03..11326e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -59,6 +59,7 @@
     private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
     private Icon mIcon = mock(Icon.class);
     private IconCompat mIconCompat = mock(IconCompat.class);
+    private View mDialogLaunchView = mock(View.class);
 
     private MediaOutputAdapter mMediaOutputAdapter;
     private MediaOutputAdapter.MediaDeviceViewHolder mViewHolder;
@@ -245,7 +246,7 @@
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
         mViewHolder.mContainerLayout.performClick();
 
-        verify(mMediaOutputController).launchBluetoothPairing();
+        verify(mMediaOutputController).launchBluetoothPairing(mViewHolder.mContainerLayout);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 14afece..794bc09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -39,7 +39,6 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.mockito.Mock
 import org.mockito.Mockito.verify
@@ -49,7 +48,6 @@
 import java.util.concurrent.Executor
 
 @SmallTest
-@Ignore("b/216286227")
 class MediaTttCommandLineHelperTest : SysuiTestCase() {
 
     private val inlineExecutor = Executor { command -> command.run() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index f05d621..ea0a5a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.graphics.drawable.Drawable
-import android.graphics.drawable.Icon
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
@@ -26,10 +25,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
@@ -39,10 +41,12 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@Ignore("b/216286227")
 class MediaTttChipControllerCommonTest : SysuiTestCase() {
     private lateinit var controllerCommon: MediaTttChipControllerCommon<MediaTttChipState>
 
+    private lateinit var fakeClock: FakeSystemClock
+    private lateinit var fakeExecutor: FakeExecutor
+
     private lateinit var appIconDrawable: Drawable
     @Mock
     private lateinit var windowManager: WindowManager
@@ -50,8 +54,11 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
-        controllerCommon = TestControllerCommon(context, windowManager)
+        appIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+        fakeClock = FakeSystemClock()
+        fakeExecutor = FakeExecutor(fakeClock)
+
+        controllerCommon = TestControllerCommon(context, windowManager, fakeExecutor)
     }
 
     @Test
@@ -71,6 +78,58 @@
     }
 
     @Test
+    fun displayChip_chipDoesNotDisappearsBeforeTimeout() {
+        controllerCommon.displayChip(getState())
+        reset(windowManager)
+
+        fakeClock.advanceTime(TIMEOUT_MILLIS - 1)
+
+        verify(windowManager, never()).removeView(any())
+    }
+
+    @Test
+    fun displayChip_chipDisappearsAfterTimeout() {
+        controllerCommon.displayChip(getState())
+        reset(windowManager)
+
+        fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
+
+        verify(windowManager).removeView(any())
+    }
+
+    @Test
+    fun displayChip_calledAgainBeforeTimeout_timeoutReset() {
+        // First, display the chip
+        controllerCommon.displayChip(getState())
+
+        // After some time, re-display the chip
+        val waitTime = 1000L
+        fakeClock.advanceTime(waitTime)
+        controllerCommon.displayChip(getState())
+
+        // Wait until the timeout for the first display would've happened
+        fakeClock.advanceTime(TIMEOUT_MILLIS - waitTime + 1)
+
+        // Verify we didn't hide the chip
+        verify(windowManager, never()).removeView(any())
+    }
+
+    @Test
+    fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() {
+        // First, display the chip
+        controllerCommon.displayChip(getState())
+
+        // After some time, re-display the chip
+        fakeClock.advanceTime(1000L)
+        controllerCommon.displayChip(getState())
+
+        // Ensure we still hide the chip eventually
+        fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
+
+        verify(windowManager).removeView(any())
+    }
+
+    @Test
     fun removeChip_chipRemoved() {
         // First, add the chip
         controllerCommon.displayChip(getState())
@@ -93,10 +152,10 @@
         controllerCommon.displayChip(getState())
         val chipView = getChipView()
 
-        val state = MediaTttChipState(PACKAGE_NAME)
+        val state = TestChipState(PACKAGE_NAME)
         controllerCommon.setIcon(state, chipView)
 
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
         assertThat(chipView.getAppIconView().contentDescription)
                 .isEqualTo(state.getAppName(context))
     }
@@ -113,13 +172,18 @@
 
     inner class TestControllerCommon(
         context: Context,
-        windowManager: WindowManager
-    ) : MediaTttChipControllerCommon<MediaTttChipState>(
-        context, windowManager, R.layout.media_ttt_chip
+        windowManager: WindowManager,
+        @Main mainExecutor: DelayableExecutor,
+        ) : MediaTttChipControllerCommon<MediaTttChipState>(
+        context, windowManager, mainExecutor, R.layout.media_ttt_chip
     ) {
         override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) {
         }
     }
+
+    inner class TestChipState(appPackageName: String?) : MediaTttChipState(appPackageName) {
+        override fun getAppIcon(context: Context) = appIconDrawable
+    }
 }
 
 private const val PACKAGE_NAME = "com.android.systemui"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 44f691c..117a6c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -17,7 +17,9 @@
 package com.android.systemui.media.taptotransfer.receiver
 
 import android.app.StatusBarManager
-import android.graphics.drawable.Icon
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
 import android.media.MediaRoute2Info
 import android.os.Handler
 import android.view.View
@@ -28,33 +30,54 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@Ignore("b/216286227")
 class MediaTttChipControllerReceiverTest : SysuiTestCase() {
     private lateinit var controllerReceiver: MediaTttChipControllerReceiver
 
     @Mock
+    private lateinit var packageManager: PackageManager
+    @Mock
+    private lateinit var applicationInfo: ApplicationInfo
+    @Mock
     private lateinit var windowManager: WindowManager
     @Mock
     private lateinit var commandQueue: CommandQueue
     private lateinit var commandQueueCallback: CommandQueue.Callbacks
+    private lateinit var fakeAppIconDrawable: Drawable
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+
+        fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+        whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
+        whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
+        whenever(packageManager.getApplicationInfo(
+            eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
+        )).thenReturn(applicationInfo)
+        context.setMockPackageManager(packageManager)
+
         controllerReceiver = MediaTttChipControllerReceiver(
-                commandQueue, context, windowManager, Handler.getMain())
+            commandQueue,
+            context,
+            windowManager,
+            FakeExecutor(FakeSystemClock()),
+            Handler.getMain()
+        )
 
         val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
         verify(commandQueue).addCallback(callbackCaptor.capture())
@@ -113,13 +136,12 @@
 
         controllerReceiver.displayChip(state)
 
-        assertThat(getChipView().getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-
+        assertThat(getChipView().getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
     }
 
     @Test
     fun displayChip_hasAppIconDrawable_iconIsDrawable() {
-        val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+        val drawable = context.getDrawable(R.drawable.ic_alarm)!!
         val state = ChipStateReceiver(PACKAGE_NAME, drawable, "appName")
 
         controllerReceiver.displayChip(state)
@@ -133,13 +155,12 @@
 
         controllerReceiver.displayChip(state)
 
-        assertThat(getChipView().getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(APP_NAME)
     }
 
     @Test
     fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() {
-        val appName = "FakeAppName"
+        val appName = "Override App Name"
         val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName)
 
         controllerReceiver.displayChip(state)
@@ -156,6 +177,7 @@
     private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
 }
 
+private const val APP_NAME = "Fake app name"
 private const val PACKAGE_NAME = "com.android.systemui"
 
 private val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index dc39893..b440064 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.media.taptotransfer.sender
 
 import android.app.StatusBarManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
 import android.media.MediaRoute2Info
 import android.view.View
 import android.view.WindowManager
@@ -28,32 +31,50 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@Ignore("b/216286227")
 class MediaTttChipControllerSenderTest : SysuiTestCase() {
     private lateinit var controllerSender: MediaTttChipControllerSender
 
     @Mock
+    private lateinit var packageManager: PackageManager
+    @Mock
+    private lateinit var applicationInfo: ApplicationInfo
+    @Mock
     private lateinit var windowManager: WindowManager
     @Mock
     private lateinit var commandQueue: CommandQueue
     private lateinit var commandQueueCallback: CommandQueue.Callbacks
+    private lateinit var fakeAppIconDrawable: Drawable
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        controllerSender = MediaTttChipControllerSender(commandQueue, context, windowManager)
+
+        fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+        whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
+        whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
+        whenever(packageManager.getApplicationInfo(
+            eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
+        )).thenReturn(applicationInfo)
+        context.setMockPackageManager(packageManager)
+
+        controllerSender = MediaTttChipControllerSender(
+            commandQueue, context, windowManager, FakeExecutor(FakeSystemClock())
+        )
 
         val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
         verify(commandQueue).addCallback(callbackCaptor.capture())
@@ -192,9 +213,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -207,9 +227,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -222,9 +241,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -237,9 +255,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -252,9 +269,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -314,9 +330,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -376,9 +391,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -451,15 +465,15 @@
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun almostCloseToStartCast() =
-        AlmostCloseToStartCast(PACKAGE_NAME, DEVICE_NAME)
+        AlmostCloseToStartCast(PACKAGE_NAME, OTHER_DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun almostCloseToEndCast() =
-        AlmostCloseToEndCast(PACKAGE_NAME, DEVICE_NAME)
+        AlmostCloseToEndCast(PACKAGE_NAME, OTHER_DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToReceiverTriggered() =
-        TransferToReceiverTriggered(PACKAGE_NAME, DEVICE_NAME)
+        TransferToReceiverTriggered(PACKAGE_NAME, OTHER_DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToThisDeviceTriggered() =
@@ -468,23 +482,24 @@
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
         TransferToReceiverSucceeded(
-            PACKAGE_NAME, DEVICE_NAME, undoCallback
+            PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
         )
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
         TransferToThisDeviceSucceeded(
-            PACKAGE_NAME, DEVICE_NAME, undoCallback
+            PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
         )
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferFailed() = TransferFailed(PACKAGE_NAME)
 }
 
-private const val DEVICE_NAME = "My Tablet"
+private const val APP_NAME = "Fake app name"
+private const val OTHER_DEVICE_NAME = "My Tablet"
 private const val PACKAGE_NAME = "com.android.systemui"
 
-private val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
+private val routeInfo = MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
     .addFeature("feature")
     .setPackageName(PACKAGE_NAME)
     .build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 73d2b0b..bb42c12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
@@ -90,6 +91,7 @@
                         mock(NavBarHelper.class),
                         mock(TaskbarDelegate.class),
                         mNavigationBarFactory,
+                        mock(StatusBarKeyguardViewManager.class),
                         mock(DumpManager.class),
                         mock(AutoHideController.class),
                         mock(LightBarController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 090ce43..48d3857 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -28,6 +28,7 @@
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
 import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
+import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -228,7 +229,8 @@
 
     @Test
     public void testHomeLongPress() {
-        mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
+        mNavigationBar.onViewAttachedToWindow(mNavigationBar
+                .createView(null, /* initialVisibility= */ true));
         mNavigationBar.onHomeLongClick(mNavigationBar.getView());
 
         verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS);
@@ -241,7 +243,8 @@
                     .setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
                     .build());
         when(mNavBarHelper.getLongPressHomeEnabled()).thenReturn(true);
-        mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
+        mNavigationBar.onViewAttachedToWindow(mNavigationBar
+                .createView(null, /* initialVisibility= */ true));
 
         mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
                 /*downTime=*/SystemClock.uptimeMillis(),
@@ -263,7 +266,8 @@
 
     @Test
     public void testRegisteredWithDispatcher() {
-        mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
+        mNavigationBar.onViewAttachedToWindow(mNavigationBar
+                .createView(null, /* initialVisibility= */ true));
         verify(mBroadcastDispatcher).registerReceiverWithHandler(
                 any(BroadcastReceiver.class),
                 any(IntentFilter.class),
@@ -283,8 +287,8 @@
         doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
         doNothing().when(defaultNavBar).checkNavBarModes();
         doNothing().when(externalNavBar).checkNavBarModes();
-        defaultNavBar.createView(null);
-        externalNavBar.createView(null);
+        defaultNavBar.createView(null, /* initialVisibility= */ true);
+        externalNavBar.createView(null, /* initialVisibility= */ true);
 
         defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true);
@@ -318,7 +322,7 @@
         doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
         doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
         doNothing().when(mNavigationBar).checkNavBarModes();
-        mNavigationBar.createView(null);
+        mNavigationBar.createView(null, /* initialVisibility= */ true);
         WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
         doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
 
@@ -354,7 +358,7 @@
 
     @Test
     public void testA11yEventAfterDetach() {
-        View v = mNavigationBar.createView(null);
+        View v = mNavigationBar.createView(null, /* initialVisibility= */ true);
         mNavigationBar.onViewAttachedToWindow(v);
         verify(mNavBarHelper).registerNavTaskStateUpdater(any(
                 NavBarHelper.NavbarTaskbarStateUpdater.class));
@@ -366,6 +370,20 @@
         mNavigationBar.updateAccessibilityStateFlags();
     }
 
+    @Test
+    public void testCreateView_initiallyVisible_viewIsVisible() {
+        mNavigationBar.createView(null, /* initialVisibility= */ true);
+
+        assertThat(mNavigationBar.getView().getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void testCreateView_initiallyNotVisible_viewIsNotVisible() {
+        mNavigationBar.createView(null, /* initialVisibility= */ false);
+
+        assertThat(mNavigationBar.getView().getVisibility()).isEqualTo(View.INVISIBLE);
+    }
+
     private NavigationBar createNavBar(Context context) {
         DeviceProvisionedController deviceProvisionedController =
                 mock(DeviceProvisionedController.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 0d65541..a2959e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -136,6 +136,8 @@
     private LocationController mLocationController;
     @Mock
     private DialogLaunchAnimator mDialogLaunchAnimator;
+    @Mock
+    private View mDialogLaunchView;
 
     private TestableResources mTestableResources;
     private InternetDialogController mInternetDialogController;
@@ -384,7 +386,8 @@
 
     @Test
     public void launchWifiNetworkDetailsSetting_withNoWifiEntryKey_doNothing() {
-        mInternetDialogController.launchWifiNetworkDetailsSetting(null /* key */);
+        mInternetDialogController.launchWifiNetworkDetailsSetting(null /* key */,
+                mDialogLaunchView);
 
         verify(mActivityStarter, never())
                 .postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
@@ -392,9 +395,11 @@
 
     @Test
     public void launchWifiNetworkDetailsSetting_withWifiEntryKey_startActivity() {
-        mInternetDialogController.launchWifiNetworkDetailsSetting("wifi_entry_key");
+        mInternetDialogController.launchWifiNetworkDetailsSetting("wifi_entry_key",
+                mDialogLaunchView);
 
-        verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
+        verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt(),
+                any());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index ed35dcb..cf97bda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -458,7 +458,8 @@
     public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
         mSeeAll.performClick();
 
-        verify(mInternetDialogController).launchNetworkSetting();
+        verify(mInternetDialogController).launchNetworkSetting(
+                mDialogView.requireViewById(R.id.see_all_layout));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index 8695b29..030c65a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -21,6 +21,7 @@
 import android.provider.Settings
 import android.testing.AndroidTestingRunner
 import android.view.View
+import android.widget.Button
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
@@ -63,6 +64,8 @@
     @Mock
     private lateinit var launchView: View
     @Mock
+    private lateinit var neutralButton: Button
+    @Mock
     private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
     @Mock
     private lateinit var uiEventLogger: UiEventLogger
@@ -130,14 +133,17 @@
 
         controller.showDialog(launchView)
 
-        verify(dialog).setNeutralButton(anyInt(), capture(clickCaptor))
+        verify(dialog)
+            .setNeutralButton(anyInt(), capture(clickCaptor), eq(false) /* dismissOnClick */)
+        `when`(dialog.getButton(DialogInterface.BUTTON_NEUTRAL)).thenReturn(neutralButton)
 
         clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
 
         verify(activityStarter)
                 .postStartActivityDismissingKeyguard(
                         argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
-                        eq(0)
+                        eq(0),
+                        eq(null)
                 )
         verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
     }
@@ -148,7 +154,8 @@
 
         controller.showDialog(launchView)
 
-        verify(dialog).setNeutralButton(anyInt(), capture(clickCaptor))
+        verify(dialog)
+            .setNeutralButton(anyInt(), capture(clickCaptor), eq(false) /* dismissOnClick */)
 
         clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 9f152e1..b7b3088 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -56,6 +56,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
+        allowTestableLooperAsMainThread();
         when(mWifiInfo.makeCopy(anyLong())).thenReturn(mWifiInfo);
         when(mWifiInfo.isPrimary()).thenReturn(true);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index f6eff82..479c271 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -100,8 +100,6 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.idle.IdleHostViewController;
-import com.android.systemui.idle.dagger.IdleViewComponent;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.media.MediaDataManager;
@@ -266,12 +264,6 @@
     @Mock
     private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
     @Mock
-    private IdleViewComponent.Factory mIdleViewComponentFactory;
-    @Mock
-    private IdleViewComponent mIdleViewComponent;
-    @Mock
-    private IdleHostViewController mIdleHostViewController;
-    @Mock
     private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
     @Mock
     private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
@@ -475,10 +467,6 @@
                 .thenReturn(mCommunalViewComponent);
         when(mCommunalViewComponent.getCommunalHostViewController())
                 .thenReturn(mCommunalHostViewController);
-        when(mIdleViewComponentFactory.build(any()))
-                .thenReturn(mIdleViewComponent);
-        when(mIdleViewComponent.getIdleHostViewController())
-                .thenReturn(mIdleHostViewController);
         when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
                 .thenReturn(mKeyguardStatusView);
         when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean()))
@@ -523,7 +511,6 @@
                 mKeyguardUserSwitcherComponentFactory,
                 mKeyguardStatusBarViewComponentFactory,
                 mCommunalViewComponentFactory,
-                mIdleViewComponentFactory,
                 mLockscreenShadeTransitionController,
                 mGroupManager,
                 mNotificationAreaController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 107ba81..8b93de5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -53,6 +53,7 @@
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -60,6 +61,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -100,6 +103,8 @@
     @Mock
     private ShadeController mShadeController;
     @Mock
+    private SysUIUnfoldComponent mSysUiUnfoldComponent;
+    @Mock
     private DreamOverlayStateController mDreamOverlayStateController;
     @Mock
     private LatencyTracker mLatencyTracker;
@@ -130,6 +135,7 @@
                 mock(NotificationMediaManager.class),
                 mKeyguardBouncerFactory,
                 mKeyguardMessageAreaFactory,
+                Optional.of(mSysUiUnfoldComponent),
                 () -> mShadeController,
                 mLatencyTracker);
         mStatusBarKeyguardViewManager.registerStatusBar(
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index e122993..75d9b7e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -275,12 +275,10 @@
             if (index >= 0) {
                 RemoteViews presentation = dataset.getFieldDialogPresentation(index);
                 if (presentation == null) {
-                    Slog.w(TAG, "fallback to presentation");
-                    presentation = dataset.getFieldPresentation(index);
-                }
-                if (presentation == null) {
-                    Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
-                            + "service didn't provide a presentation for it on " + dataset);
+                    if (sDebug) {
+                        Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
+                                + "service didn't provide a presentation for it on " + dataset);
+                    }
                     continue;
                 }
                 final View view;
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 4afa96c..bc1f28d 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -37,6 +37,7 @@
 import android.view.Display;
 import android.window.DisplayWindowPolicyController;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.BlockedAppStreamingActivity;
 
 import java.util.List;
@@ -75,9 +76,11 @@
     private final ArraySet<ComponentName> mAllowedActivities;
     @Nullable
     private final ArraySet<ComponentName> mBlockedActivities;
+    private final Object mGenericWindowPolicyControllerLock = new Object();
     private Consumer<ActivityInfo> mActivityBlockedCallback;
 
     @NonNull
+    @GuardedBy("mGenericWindowPolicyControllerLock")
     final ArraySet<Integer> mRunningUids = new ArraySet<>();
     @Nullable private final ActivityListener mActivityListener;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -149,11 +152,13 @@
 
     @Override
     public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
-        mRunningUids.clear();
-        mRunningUids.addAll(runningUids);
-        if (mActivityListener != null && mRunningUids.isEmpty()) {
-            // Post callback on the main thread so it doesn't block activity launching
-            mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY));
+        synchronized (mGenericWindowPolicyControllerLock) {
+            mRunningUids.clear();
+            mRunningUids.addAll(runningUids);
+            if (mActivityListener != null && mRunningUids.isEmpty()) {
+                // Post callback on the main thread so it doesn't block activity launching
+                mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY));
+            }
         }
         if (mRunningAppsChangedListener != null) {
             mRunningAppsChangedListener.onRunningAppsChanged(runningUids);
@@ -165,7 +170,9 @@
      * this controller.
      */
     boolean containsUid(int uid) {
-        return mRunningUids.contains(uid);
+        synchronized (mGenericWindowPolicyControllerLock) {
+            return mRunningUids.contains(uid);
+        }
     }
 
     private boolean canContainActivity(ActivityInfo activityInfo, int windowFlags,
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 387d911..c0a904f 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -235,6 +235,10 @@
                 });
                 mPerDisplayWakelocks.clear();
             }
+            if (mVirtualAudioController != null) {
+                mVirtualAudioController.stopListening();
+                mVirtualAudioController = null;
+            }
         }
         mListener.onClose(mAssociationInfo.getId());
         mAppToken.unlinkToDeath(this, 0);
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index d218af3..6986d3b 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -36,11 +36,8 @@
 import com.android.internal.os.IBinaryTransparencyService;
 import com.android.internal.util.FrameworkStatsLog;
 
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.IOException;
 import java.io.PrintWriter;
-import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -434,7 +431,7 @@
                     entry.setValue(packageInfo.lastUpdateTime);
 
                     // compute the digest for the updated package
-                    String sha256digest = computeSha256DigestOfFile(
+                    String sha256digest = PackageUtils.computeSha256DigestForLargeFile(
                             packageInfo.applicationInfo.sourceDir);
                     if (sha256digest == null) {
                         Slog.e(TAG, "Failed to compute SHA256sum for file at "
@@ -471,7 +468,7 @@
             ApplicationInfo appInfo = packageInfo.applicationInfo;
 
             // compute SHA256 for these APEXs
-            String sha256digest = computeSha256DigestOfFile(appInfo.sourceDir);
+            String sha256digest = PackageUtils.computeSha256DigestForLargeFile(appInfo.sourceDir);
             if (sha256digest == null) {
                 Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
                         packageInfo.packageName));
@@ -506,7 +503,8 @@
                 ApplicationInfo appInfo = packageInfo.applicationInfo;
 
                 // compute SHA256 digest for these modules
-                String sha256digest = computeSha256DigestOfFile(appInfo.sourceDir);
+                String sha256digest = PackageUtils.computeSha256DigestForLargeFile(
+                        appInfo.sourceDir);
                 if (sha256digest == null) {
                     Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
                             packageName));
@@ -525,16 +523,4 @@
         }
     }
 
-    @Nullable
-    private String computeSha256DigestOfFile(@NonNull String pathToFile) {
-        File apexFile = new File(pathToFile);
-
-        try {
-            byte[] apexFileBytes = Files.readAllBytes(apexFile.toPath());
-            return PackageUtils.computeSha256Digest(apexFileBytes);
-        } catch (IOException e) {
-            Slog.e(TAG, String.format("I/O error occurs when reading from %s", pathToFile));
-            return null;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 8e53101..16ff167 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
 import static android.app.ActivityManager.UID_OBSERVER_GONE;
+import static android.os.Process.SYSTEM_UID;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -261,6 +262,37 @@
         }
     }
 
+    /** Returns information about pinned files and sizes for StatsPullAtomService. */
+    public List<PinnedFileStats> dumpDataForStatsd() {
+        List<PinnedFileStats> pinnedFileStats = new ArrayList<>();
+        synchronized (PinnerService.this) {
+            for (PinnedFile pinnedFile : mPinnedFiles) {
+                pinnedFileStats.add(new PinnedFileStats(SYSTEM_UID, pinnedFile));
+            }
+
+            for (int key : mPinnedApps.keySet()) {
+                PinnedApp app = mPinnedApps.get(key);
+                for (PinnedFile pinnedFile : mPinnedApps.get(key).mFiles) {
+                    pinnedFileStats.add(new PinnedFileStats(app.uid, pinnedFile));
+                }
+            }
+        }
+        return pinnedFileStats;
+    }
+
+    /** Wrapper class for statistics for a pinned file. */
+    public static class PinnedFileStats {
+        public final int uid;
+        public final String filename;
+        public final int sizeKb;
+
+        protected PinnedFileStats(int uid, PinnedFile file) {
+            this.uid = uid;
+            this.filename = file.fileName.substring(file.fileName.lastIndexOf('/') + 1);
+            this.sizeKb = file.bytesPinned / 1024;
+        }
+    }
+
     /**
      * Handler for on start pinning message
      */
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 90aefe0..d239c02 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -230,7 +230,9 @@
                 "Caller did not have permission while calling " + methodName);
         userId = handleIncomingUser(userId, methodName);
         synchronized (mLock) {
-            if (!checkUserStatesExist(userId, methodName)) {
+            // Don't log as this method can be called before user states exist as part of the
+            // force-stop check.
+            if (!checkUserStatesExist(userId, methodName, /* shouldLog= */ false)) {
                 return false;
             }
             final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
@@ -238,8 +240,6 @@
             if (pkgState == null
                     || !mPackageManagerInternal.canQueryPackage(
                             Binder.getCallingUid(), packageName)) {
-                Slog.e(TAG, TextUtils.formatSimple("Package %s is not installed for user %s",
-                        packageName, userId));
                 return false;
             }
             return pkgState.hibernated;
@@ -289,7 +289,7 @@
                 "Caller does not have MANAGE_APP_HIBERNATION permission.");
         final int realUserId = handleIncomingUser(userId, methodName);
         synchronized (mLock) {
-            if (!checkUserStatesExist(realUserId, methodName)) {
+            if (!checkUserStatesExist(realUserId, methodName, /* shouldLog= */ true)) {
                 return;
             }
             final Map<String, UserLevelState> packageStates = mUserStates.get(realUserId);
@@ -382,7 +382,7 @@
                 "Caller does not have MANAGE_APP_HIBERNATION permission.");
         userId = handleIncomingUser(userId, methodName);
         synchronized (mLock) {
-            if (!checkUserStatesExist(userId, methodName)) {
+            if (!checkUserStatesExist(userId, methodName, /* shouldLog= */ true)) {
                 return hibernatingPackages;
             }
             Map<String, UserLevelState> userStates = mUserStates.get(userId);
@@ -419,7 +419,7 @@
                 "Caller does not have MANAGE_APP_HIBERNATION permission.");
         userId = handleIncomingUser(userId, methodName);
         synchronized (mLock) {
-            if (!checkUserStatesExist(userId, methodName)) {
+            if (!checkUserStatesExist(userId, methodName, /* shouldLog= */ true)) {
                 return statsMap;
             }
             final Map<String, UserLevelState> userPackageStates = mUserStates.get(userId);
@@ -431,7 +431,7 @@
                 }
                 if (!mGlobalHibernationStates.containsKey(pkgName)
                         || !userPackageStates.containsKey(pkgName)) {
-                    Slog.w(TAG, String.format(
+                    Slog.w(TAG, TextUtils.formatSimple(
                             "No hibernation state associated with package %s user %d. Maybe"
                                     + "the package was uninstalled? ", pkgName, userId));
                     continue;
@@ -585,7 +585,7 @@
                 PackageInfo pkgInfo = installedPackages.get(packageName);
                 UserLevelState currentState = diskStates.get(i);
                 if (pkgInfo == null) {
-                    Slog.w(TAG, String.format(
+                    Slog.w(TAG, TextUtils.formatSimple(
                             "No hibernation state associated with package %s user %d. Maybe"
                                     + "the package was uninstalled? ", packageName, userId));
                     continue;
@@ -633,7 +633,7 @@
             for (int i = 0, size = diskStates.size(); i < size; i++) {
                 GlobalLevelState state = diskStates.get(i);
                 if (!installedPackages.contains(state.packageName)) {
-                    Slog.w(TAG, String.format(
+                    Slog.w(TAG, TextUtils.formatSimple(
                             "No hibernation state associated with package %s. Maybe the "
                                     + "package was uninstalled? ", state.packageName));
                     continue;
@@ -742,18 +742,24 @@
      *
      * @param userId user to check
      * @param methodName method name that is calling. Used for logging purposes.
+     * @param shouldLog whether we should log why the user state doesn't exist
      * @return true if user states exist
      */
     @GuardedBy("mLock")
-    private boolean checkUserStatesExist(int userId, String methodName) {
+    private boolean checkUserStatesExist(int userId, String methodName, boolean shouldLog) {
         if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
-            Slog.e(TAG, String.format(
-                    "Attempt to call %s on stopped or nonexistent user %d", methodName, userId));
+            if (shouldLog) {
+                Slog.w(TAG, TextUtils.formatSimple(
+                        "Attempt to call %s on stopped or nonexistent user %d",
+                        methodName, userId));
+            }
             return false;
         }
         if (!mUserStates.contains(userId)) {
-            Slog.w(TAG, String.format(
-                    "Attempt to call %s before states have been read from disk", methodName));
+            if (shouldLog) {
+                Slog.w(TAG, TextUtils.formatSimple(
+                        "Attempt to call %s before states have been read from disk", methodName));
+            }
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index d2fa386..567d1ae 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -30,7 +30,7 @@
 import android.app.ActivityThread;
 import android.attention.AttentionManagerInternal;
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
-import android.attention.AttentionManagerInternal.ProximityCallbackInternal;
+import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -59,7 +59,7 @@
 import android.service.attention.AttentionService.AttentionSuccessCodes;
 import android.service.attention.IAttentionCallback;
 import android.service.attention.IAttentionService;
-import android.service.attention.IProximityCallback;
+import android.service.attention.IProximityUpdateCallback;
 import android.text.TextUtils;
 import android.util.Slog;
 
@@ -336,7 +336,7 @@
      * @return {@code true} if the framework was able to dispatch the request
      */
     @VisibleForTesting
-    boolean onStartProximityUpdates(ProximityCallbackInternal callbackInternal) {
+    boolean onStartProximityUpdates(ProximityUpdateCallbackInternal callbackInternal) {
         Objects.requireNonNull(callbackInternal);
         if (!mIsServiceEnabled) {
             Slog.w(LOG_TAG, "Trying to call onProximityUpdate() on an unsupported device.");
@@ -385,7 +385,7 @@
 
     /** Cancels the specified proximity registration. */
     @VisibleForTesting
-    void onStopProximityUpdates(ProximityCallbackInternal callbackInternal) {
+    void onStopProximityUpdates(ProximityUpdateCallbackInternal callbackInternal) {
         synchronized (mLock) {
             if (mCurrentProximityUpdate == null
                     || !mCurrentProximityUpdate.mCallbackInternal.equals(callbackInternal)
@@ -506,12 +506,12 @@
 
         @Override
         public boolean onStartProximityUpdates(
-                ProximityCallbackInternal callback) {
+                ProximityUpdateCallbackInternal callback) {
             return AttentionManagerService.this.onStartProximityUpdates(callback);
         }
 
         @Override
-        public void onStopProximityUpdates(ProximityCallbackInternal callback) {
+        public void onStopProximityUpdates(ProximityUpdateCallbackInternal callback) {
             AttentionManagerService.this.onStopProximityUpdates(callback);
         }
     }
@@ -635,13 +635,13 @@
 
     @VisibleForTesting
     final class ProximityUpdate {
-        private final ProximityCallbackInternal mCallbackInternal;
-        private final IProximityCallback mIProximityCallback;
+        private final ProximityUpdateCallbackInternal mCallbackInternal;
+        private final IProximityUpdateCallback mIProximityUpdateCallback;
         private boolean mStartedUpdates;
 
-        ProximityUpdate(ProximityCallbackInternal callbackInternal) {
+        ProximityUpdate(ProximityUpdateCallbackInternal callbackInternal) {
             mCallbackInternal = callbackInternal;
-            mIProximityCallback = new IProximityCallback.Stub() {
+            mIProximityUpdateCallback = new IProximityUpdateCallback.Stub() {
                 @Override
                 public void onProximityUpdate(double distance) {
                     synchronized (mLock) {
@@ -664,7 +664,7 @@
                     return false;
                 }
                 try {
-                    mService.onStartProximityUpdates(mIProximityCallback);
+                    mService.onStartProximityUpdates(mIProximityUpdateCallback);
                     mStartedUpdates = true;
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
@@ -758,7 +758,8 @@
         if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) {
             if (mService != null) {
                 try {
-                    mService.onStartProximityUpdates(mCurrentProximityUpdate.mIProximityCallback);
+                    mService.onStartProximityUpdates(
+                            mCurrentProximityUpdate.mIProximityUpdateCallback);
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
                 }
@@ -913,7 +914,7 @@
             }
         }
 
-        class TestableProximityCallbackInternal extends ProximityCallbackInternal {
+        class TestableProximityUpdateCallbackInternal extends ProximityUpdateCallbackInternal {
             private double mLastCallbackCode = PROXIMITY_UNKNOWN;
 
             @Override
@@ -932,8 +933,8 @@
 
         final TestableAttentionCallbackInternal mTestableAttentionCallback =
                 new TestableAttentionCallbackInternal();
-        final TestableProximityCallbackInternal mTestableProximityCallback =
-                new TestableProximityCallbackInternal();
+        final TestableProximityUpdateCallbackInternal mTestableProximityUpdateCallback =
+                new TestableProximityUpdateCallbackInternal();
 
         @Override
         public int onCommand(@Nullable final String cmd) {
@@ -964,8 +965,8 @@
                         return cmdClearTestableAttentionService();
                     case "getLastTestCallbackCode":
                         return cmdGetLastTestCallbackCode();
-                    case "getLastTestProximityCallbackCode":
-                        return cmdGetLastTestProximityCallbackCode();
+                    case "getLastTestProximityUpdateCallbackCode":
+                        return cmdGetLastTestProximityUpdateCallbackCode();
                     default:
                         return handleDefaultCommands(cmd);
                 }
@@ -990,7 +991,7 @@
         private int cmdClearTestableAttentionService() {
             sTestAttentionServicePackage = "";
             mTestableAttentionCallback.reset();
-            mTestableProximityCallback.reset();
+            mTestableProximityUpdateCallback.reset();
             resetStates();
             return 0;
         }
@@ -1011,14 +1012,14 @@
 
         private int cmdCallOnStartProximityUpdates() {
             final PrintWriter out = getOutPrintWriter();
-            boolean calledSuccessfully = onStartProximityUpdates(mTestableProximityCallback);
+            boolean calledSuccessfully = onStartProximityUpdates(mTestableProximityUpdateCallback);
             out.println(calledSuccessfully ? "true" : "false");
             return 0;
         }
 
         private int cmdCallOnStopProximityUpdates() {
             final PrintWriter out = getOutPrintWriter();
-            onStopProximityUpdates(mTestableProximityCallback);
+            onStopProximityUpdates(mTestableProximityUpdateCallback);
             out.println("true");
             return 0;
         }
@@ -1036,9 +1037,9 @@
             return 0;
         }
 
-        private int cmdGetLastTestProximityCallbackCode() {
+        private int cmdGetLastTestProximityUpdateCallbackCode() {
             final PrintWriter out = getOutPrintWriter();
-            out.println(mTestableProximityCallback.getLastCallbackCode());
+            out.println(mTestableProximityUpdateCallback.getLastCallbackCode());
             return 0;
         }
 
@@ -1081,7 +1082,7 @@
             out.println(
                     "       := true, if the request was successfully dispatched to the service "
                             + "implementation."
-                            + " (to see the result, call getLastTestProximityCallbackCode)");
+                            + " (to see the result, call getLastTestProximityUpdateCallbackCode)");
             out.println("       := false, otherwise");
             out.println("  call onStopProximityUpdates: Cancels proximity updates");
             out.println("  getLastTestCallbackCode");
@@ -1089,7 +1090,7 @@
             out.println(
                     "       := An integer, representing the last callback code received from the "
                             + "bounded implementation. If none, it will return -1");
-            out.println("  getLastTestProximityCallbackCode");
+            out.println("  getLastTestProximityUpdateCallbackCode");
             out.println("  ---returns:");
             out.println(
                     "       := A double, representing the last proximity value received from the "
diff --git a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
index cb93cc8..f529c4c 100644
--- a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
+++ b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
@@ -98,6 +98,13 @@
         return ColorDisplayManager.isColorTransformAccelerated(context);
     }
 
+    @Override
+    public void setActivated(Boolean isActivated) {
+        super.setActivated(isActivated);
+        Slog.i(ColorDisplayService.TAG, (isActivated != null && isActivated)
+                ? "Turning on reduce bright colors" : "Turning off reduce bright colors");
+    }
+
     public int getStrength() {
         return mStrength;
     }
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index 34614d5..490e00e 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -329,6 +329,20 @@
 
             if (mStart) {
 
+                ActivityManagerInternal ami = LocalServices.getService(
+                        ActivityManagerInternal.class);
+                boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(mUid);
+
+                // The instrumented apks only run for testing, so we don't check user permission.
+                if (isCallerInstrumented) {
+                    try {
+                        getLogdService().approve(mUid, mGid, mPid, mFd);
+                    } catch (RemoteException e) {
+                        e.printStackTrace();
+                    }
+                    return;
+                }
+
                 // TODO Temporarily approve all the requests to unblock testing failures.
                 try {
                     getLogdService().approve(mUid, mGid, mPid, mFd);
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index 410fa97..da22b17 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -37,6 +37,8 @@
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedArraySet;
 
+import java.util.Collections;
+import java.util.Map;
 import java.util.Objects;
 
 @DataClass(genConstructor = false, genBuilder = false, genEqualsHashCode = true)
@@ -540,6 +542,13 @@
         return this;
     }
 
+    @NonNull
+    @Override
+    public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() {
+        return mSharedLibraryOverlayPaths == null
+                ? Collections.emptyMap() : mSharedLibraryOverlayPaths;
+    }
+
     @Override
     public boolean equals(@Nullable Object o) {
         // You can override field equality logic by defining either of the methods like:
@@ -703,11 +712,6 @@
     }
 
     @DataClass.Generated.Member
-    public @Nullable WatchedArrayMap<String,OverlayPaths> getSharedLibraryOverlayPaths() {
-        return mSharedLibraryOverlayPaths;
-    }
-
-    @DataClass.Generated.Member
     public @Nullable String getSplashScreenTheme() {
         return mSplashScreenTheme;
     }
@@ -774,10 +778,10 @@
     }
 
     @DataClass.Generated(
-            time = 1644638242940L,
+            time = 1645040852569L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
-            inputSignatures = "protected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate  long mCeDataInode\nprivate  boolean mInstalled\nprivate  boolean mStopped\nprivate  boolean mNotLaunched\nprivate  boolean mHidden\nprivate  int mDistractionFlags\nprivate  boolean mInstantApp\nprivate  boolean mVirtualPreload\nprivate  int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate  long mFirstInstallTime\nprivate final @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate  void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic  boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic  void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic  com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic  com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTime(long)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+            inputSignatures = "protected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate  long mCeDataInode\nprivate  boolean mInstalled\nprivate  boolean mStopped\nprivate  boolean mNotLaunched\nprivate  boolean mHidden\nprivate  int mDistractionFlags\nprivate  boolean mInstantApp\nprivate  boolean mVirtualPreload\nprivate  int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate  long mFirstInstallTime\nprivate final @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate  void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic  boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic  void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic  com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic  com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTime(long)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 18c45e4..4ad6ed1 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -33,6 +33,7 @@
 import android.location.LocationManagerInternal;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.PackageTagsList;
 import android.os.Process;
@@ -342,8 +343,11 @@
                     + Intent.ACTION_ACTIVITY_RECOGNIZER +  ", ignoring!");
             return;
         }
-        final String tagsList = resolvedService.serviceInfo.metaData.getString(
-                ACTIVITY_RECOGNITION_TAGS);
+        final Bundle metaData = resolvedService.serviceInfo.metaData;
+        if (metaData == null) {
+            return;
+        }
+        final String tagsList = metaData.getString(ACTIVITY_RECOGNITION_TAGS);
         if (!TextUtils.isEmpty(tagsList)) {
             PackageTagsList packageTagsList = new PackageTagsList.Builder(1).add(
                     resolvedService.serviceInfo.packageName,
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 70a030d..bd58472 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1177,7 +1177,10 @@
     @Override
     public void onBootPhase(int phase) {
         synchronized (mLock) {
-            if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            if (phase == PHASE_SYSTEM_SERVICES_READY) {
+                systemReady();
+
+            } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                 incrementBootCount();
 
             } else if (phase == PHASE_BOOT_COMPLETED) {
@@ -1203,7 +1206,7 @@
         }
     }
 
-    public void systemReady() {
+    private void systemReady() {
         synchronized (mLock) {
             mSystemReady = true;
             mDreamManager = getLocalService(DreamManagerInternal.class);
@@ -2684,8 +2687,8 @@
     @GuardedBy("mLock")
     private void updateUserActivitySummaryLocked(long now, int dirty) {
         // Update the status of the user activity timeout timer.
-        if ((dirty & (DIRTY_DISPLAY_GROUP_WAKEFULNESS | DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY
-                | DIRTY_WAKEFULNESS | DIRTY_SETTINGS | DIRTY_ATTENTIVE)) == 0) {
+        if ((dirty & (DIRTY_DISPLAY_GROUP_WAKEFULNESS | DIRTY_WAKE_LOCKS
+                | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) == 0) {
             return;
         }
         mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
@@ -2772,11 +2775,6 @@
                             screenDimDuration);
                 }
 
-                if (isAttentiveTimeoutExpired(powerGroup, now)) {
-                    groupUserActivitySummary = 0;
-                    groupNextTimeout = -1;
-                }
-
                 hasUserActivitySummary |= groupUserActivitySummary != 0;
 
                 if (nextTimeout == -1) {
@@ -3132,7 +3130,7 @@
                     Message msg = mHandler.obtainMessage(MSG_SANDMAN);
                     msg.arg1 = powerGroup.getGroupId();
                     msg.setAsynchronous(true);
-                    mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+                    mHandler.sendMessage(msg);
                 }
             }
         }
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 ffcb2bd..b4613a7 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -276,7 +276,7 @@
     }
 
     /**
-     * Called by {@link PowerManagerService#systemReady}, *with no lock held.*
+     * Called by {@link PowerManagerService#onBootPhase}, *with no lock held.*
      */
     public void systemReady() {
         ConcurrentUtils.wtfIfLockHeld(TAG, mLock);
diff --git a/services/core/java/com/android/server/sensorprivacy/PersistedState.java b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
index 06f5fc0..e79efdb8 100644
--- a/services/core/java/com/android/server/sensorprivacy/PersistedState.java
+++ b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
@@ -296,7 +296,7 @@
                 SensorState sensorState = states.valueAt(i);
 
                 // Do not persist hardware toggle states. Will be restored on reboot
-                if (userSensor.mType != SensorPrivacyManager.ToggleTypes.SOFTWARE) {
+                if (userSensor.mType != SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE) {
                     continue;
                 }
 
@@ -478,7 +478,7 @@
                 for (int j = 0; j < numSensors; j++) {
                     int sensor = userIndividualEnabled.keyAt(j);
                     SensorState sensorState = userIndividualEnabled.valueAt(j);
-                    result.addState(SensorPrivacyManager.ToggleTypes.SOFTWARE,
+                    result.addState(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
                             userId, sensor, sensorState.getState(), sensorState.getLastChange());
                 }
             }
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 358f69e..a8e2d43 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -39,8 +39,8 @@
 import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
 import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
 import static android.hardware.SensorPrivacyManager.Sources.SHELL;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.HARDWARE;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.SOFTWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE;
 import static android.os.UserHandle.USER_NULL;
 import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
 
@@ -310,11 +310,12 @@
             // Reset sensor privacy when restriction is added
             if (!prevRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)
                     && newRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)) {
-                setToggleSensorPrivacyUnchecked(SOFTWARE, userId, OTHER, CAMERA, false);
+                setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, OTHER, CAMERA, false);
             }
             if (!prevRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)
                     && newRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)) {
-                setToggleSensorPrivacyUnchecked(SOFTWARE, userId, OTHER, MICROPHONE, false);
+                setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, OTHER, MICROPHONE,
+                        false);
             }
         }
 
@@ -565,7 +566,7 @@
          */
         private String getSensorUseActivityName(ArraySet<Integer> sensors) {
             for (Integer sensor : sensors) {
-                if (isToggleSensorPrivacyEnabled(HARDWARE, sensor)) {
+                if (isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE, sensor)) {
                     return mContext.getResources().getString(
                             R.string.config_sensorUseStartedActivity_hwToggle);
                 }
@@ -691,7 +692,7 @@
                 return;
             }
 
-            setToggleSensorPrivacyUnchecked(SOFTWARE, userId, source, sensor, enable);
+            setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, source, sensor, enable);
         }
 
         private void setToggleSensorPrivacyUnchecked(int toggleType, int userId, int source,
@@ -866,8 +867,8 @@
 
         @Override
         public boolean isCombinedToggleSensorPrivacyEnabled(int sensor) {
-            return isToggleSensorPrivacyEnabled(SOFTWARE, sensor) || isToggleSensorPrivacyEnabled(
-                    HARDWARE, sensor);
+            return isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor)
+                    || isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE, sensor);
         }
 
         private boolean isToggleSensorPrivacyEnabledInternal(int userId, int toggleType,
@@ -879,7 +880,7 @@
 
         @Override
         public boolean supportsSensorToggle(int toggleType, int sensor) {
-            if (toggleType == SOFTWARE) {
+            if (toggleType == TOGGLE_TYPE_SOFTWARE) {
                 if (sensor == MICROPHONE) {
                     return mContext.getResources()
                             .getBoolean(R.bool.config_supportsMicToggle);
@@ -887,7 +888,7 @@
                     return mContext.getResources()
                             .getBoolean(R.bool.config_supportsCamToggle);
                 }
-            } else if (toggleType == SensorPrivacyManager.ToggleTypes.HARDWARE) {
+            } else if (toggleType == TOGGLE_TYPE_HARDWARE) {
                 if (sensor == MICROPHONE) {
                     return mContext.getResources()
                             .getBoolean(R.bool.config_supportsHardwareMicToggle);
@@ -1003,37 +1004,41 @@
             final int hwToggleIdx = 1;
             // Get SW toggles state
             mSensorPrivacyStateController.atomic(() -> {
-                prevMicState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, SOFTWARE,
-                        MICROPHONE);
-                prevCamState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, SOFTWARE,
-                        CAMERA);
-                micState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, SOFTWARE,
-                        MICROPHONE);
-                camState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, SOFTWARE, CAMERA);
+                prevMicState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_SOFTWARE, MICROPHONE);
+                prevCamState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_SOFTWARE, CAMERA);
+                micState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_SOFTWARE, MICROPHONE);
+                camState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_SOFTWARE, CAMERA);
             });
             // Get HW toggles state
             mSensorPrivacyStateController.atomic(() -> {
-                prevMicState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, HARDWARE,
-                        MICROPHONE);
-                prevCamState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, HARDWARE,
-                        CAMERA);
-                micState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, HARDWARE,
-                        MICROPHONE);
-                camState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, HARDWARE, CAMERA);
+                prevMicState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_HARDWARE, MICROPHONE);
+                prevCamState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_HARDWARE, CAMERA);
+                micState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_HARDWARE, MICROPHONE);
+                camState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_HARDWARE, CAMERA);
             });
 
             if (from == USER_NULL || prevMicState[swToggleIdx] != micState[swToggleIdx]
                     || prevMicState[hwToggleIdx] != micState[hwToggleIdx]) {
-                mHandler.handleSensorPrivacyChanged(to, SOFTWARE, MICROPHONE,
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_SOFTWARE, MICROPHONE,
                         micState[swToggleIdx]);
-                mHandler.handleSensorPrivacyChanged(to, HARDWARE, MICROPHONE,
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_HARDWARE, MICROPHONE,
                         micState[hwToggleIdx]);
                 setGlobalRestriction(MICROPHONE, micState[swToggleIdx] || micState[hwToggleIdx]);
             }
             if (from == USER_NULL || prevCamState[swToggleIdx] != camState[swToggleIdx]
                     || prevCamState[hwToggleIdx] != camState[hwToggleIdx]) {
-                mHandler.handleSensorPrivacyChanged(to, SOFTWARE, CAMERA, camState[swToggleIdx]);
-                mHandler.handleSensorPrivacyChanged(to, HARDWARE, CAMERA, camState[hwToggleIdx]);
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_SOFTWARE, CAMERA,
+                        camState[swToggleIdx]);
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_HARDWARE, CAMERA,
+                        camState[hwToggleIdx]);
                 setGlobalRestriction(CAMERA, camState[swToggleIdx] || camState[hwToggleIdx]);
             }
         }
@@ -1437,7 +1442,7 @@
         public boolean isSensorPrivacyEnabled(int userId, int sensor) {
             return SensorPrivacyService.this
                     .mSensorPrivacyServiceImpl.isToggleSensorPrivacyEnabledInternal(userId,
-                            SOFTWARE, sensor);
+                            TOGGLE_TYPE_SOFTWARE, sensor);
         }
 
         @Override
@@ -1487,10 +1492,12 @@
             userId = (userId == UserHandle.USER_CURRENT ? mCurrentUser : userId);
             final int realUserId = (userId == UserHandle.USER_NULL ? mContext.getUserId() : userId);
 
-            sps.setToggleSensorPrivacyUnchecked(HARDWARE, realUserId, OTHER, sensor, enable);
+            sps.setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_HARDWARE, realUserId, OTHER, sensor,
+                    enable);
             // Also disable the SW toggle when disabling the HW toggle
             if (!enable) {
-                sps.setToggleSensorPrivacyUnchecked(SOFTWARE, realUserId, OTHER, sensor, enable);
+                sps.setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, realUserId, OTHER, sensor,
+                        enable);
             }
         }
     }
@@ -1546,9 +1553,9 @@
                 if (!mIsInEmergencyCall) {
                     mIsInEmergencyCall = true;
                     if (mSensorPrivacyServiceImpl
-                            .isToggleSensorPrivacyEnabled(SOFTWARE, MICROPHONE)) {
+                            .isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, MICROPHONE)) {
                         mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked(
-                                SOFTWARE, mCurrentUser, OTHER, MICROPHONE, false);
+                                TOGGLE_TYPE_SOFTWARE, mCurrentUser, OTHER, MICROPHONE, false);
                         mMicUnmutedForEmergencyCall = true;
                     } else {
                         mMicUnmutedForEmergencyCall = false;
@@ -1574,7 +1581,7 @@
                     mIsInEmergencyCall = false;
                     if (mMicUnmutedForEmergencyCall) {
                         mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked(
-                                SOFTWARE, mCurrentUser, OTHER, MICROPHONE, true);
+                                TOGGLE_TYPE_SOFTWARE, mCurrentUser, OTHER, MICROPHONE, true);
                         mMicUnmutedForEmergencyCall = false;
                     }
                 }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 8ec0556..1b15351 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -197,6 +197,8 @@
 import com.android.server.BinderCallsStatsService;
 import com.android.server.LocalManagerRegistry;
 import com.android.server.LocalServices;
+import com.android.server.PinnerService;
+import com.android.server.PinnerService.PinnedFileStats;
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
@@ -727,6 +729,8 @@
                         return pullAccessibilityFloatingMenuStatsLocked(atomTag, data);
                     case FrameworkStatsLog.MEDIA_CAPABILITIES:
                         return pullMediaCapabilitiesStats(atomTag, data);
+                    case FrameworkStatsLog.PINNED_FILE_SIZES_PER_PACKAGE:
+                        return pullSystemServerPinnerStats(atomTag, data);
                     case FrameworkStatsLog.PENDING_INTENTS_PER_PACKAGE:
                         return pullPendingIntentsPerPackage(atomTag, data);
                     default:
@@ -926,6 +930,7 @@
         registerAccessibilityFloatingMenuStats();
         registerMediaCapabilitiesStats();
         registerPendingIntentsPerPackagePuller();
+        registerPinnerServiceStats();
     }
 
     private void initAndRegisterNetworkStatsPullers() {
@@ -4607,6 +4612,26 @@
         return StatsManager.PULL_SUCCESS;
     }
 
+    private void registerPinnerServiceStats() {
+        int tagId = FrameworkStatsLog.PINNED_FILE_SIZES_PER_PACKAGE;
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl
+        );
+    }
+
+    int pullSystemServerPinnerStats(int atomTag, List<StatsEvent> pulledData) {
+        PinnerService pinnerService = LocalServices.getService(PinnerService.class);
+        List<PinnedFileStats> pinnedFileStats = pinnerService.dumpDataForStatsd();
+        for (PinnedFileStats pfstats : pinnedFileStats) {
+            pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+                    pfstats.uid, pfstats.filename, pfstats.sizeKb));
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
     private byte[] toBytes(List<Integer> audioEncodings) {
         ProtoOutputStream protoOutputStream = new ProtoOutputStream();
         for (int audioEncoding : audioEncodings) {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 8b80b4a..597f7f2 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -78,6 +78,7 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.util.ArraySet;
 import android.util.Slog;
 
@@ -163,6 +164,14 @@
 public class VcnGatewayConnection extends StateMachine {
     private static final String TAG = VcnGatewayConnection.class.getSimpleName();
 
+    // Matches DataConnection.NETWORK_TYPE private constant, and magic string from
+    // ConnectivityManager#getNetworkTypeName()
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final String NETWORK_INFO_NETWORK_TYPE_STRING = "MOBILE";
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final String NETWORK_INFO_EXTRA_INFO = "VCN";
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
 
@@ -1631,6 +1640,12 @@
             final NetworkAgentConfig nac =
                     new NetworkAgentConfig.Builder()
                             .setLegacyType(ConnectivityManager.TYPE_MOBILE)
+                            .setLegacyTypeName(NETWORK_INFO_NETWORK_TYPE_STRING)
+                            .setLegacySubType(TelephonyManager.NETWORK_TYPE_UNKNOWN)
+                            .setLegacySubTypeName(
+                                    TelephonyManager.getNetworkTypeName(
+                                            TelephonyManager.NETWORK_TYPE_UNKNOWN))
+                            .setLegacyExtraInfo(NETWORK_INFO_EXTRA_INFO)
                             .build();
 
             final VcnNetworkAgent agent =
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index fdd9913..1f1f40b 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -70,32 +70,42 @@
 
     private static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
 
-    /** Callbacks for playing a {@link Vibration}. */
-    interface VibrationCallbacks {
+    /** Calls into VibratorManager functionality needed for playing a {@link Vibration}. */
+    interface VibratorManagerHooks {
 
         /**
-         * Callback triggered before starting a synchronized vibration step. This will be called
-         * with {@code requiredCapabilities = 0} if no synchronization is required.
+         * Request the manager to prepare for triggering a synchronized vibration step.
          *
          * @param requiredCapabilities The required syncing capabilities for this preparation step.
-         *                             Expects a combination of values from
+         *                             Expect CAP_SYNC and a combination of values from
          *                             IVibratorManager.CAP_PREPARE_* and
          *                             IVibratorManager.CAP_MIXED_TRIGGER_*.
          * @param vibratorIds          The id of the vibrators to be prepared.
          */
         boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds);
 
-        /** Callback triggered after synchronized vibrations were prepared. */
+        /**
+         * Request the manager to trigger a synchronized vibration. The vibration must already
+         * have been prepared with {@link #prepareSyncedVibration}.
+         */
         boolean triggerSyncedVibration(long vibrationId);
 
-        /** Callback triggered to cancel a prepared synced vibration. */
+        /** Tell the manager to cancel a synced vibration. */
         void cancelSyncedVibration();
 
-        /** Callback triggered when the vibration is complete. */
+        /**
+         * Tell the manager that the currently active vibration has completed its vibration, from
+         * the perspective of the Effect. However, the VibrationThread may still be continuing with
+         * cleanup tasks, and should not be given new work until {@link #onVibrationThreadReleased}
+         * is called.
+         */
         void onVibrationCompleted(long vibrationId, Vibration.Status status);
 
-        /** Callback triggered when the vibrators are released after the thread is complete. */
-        void onVibratorsReleased();
+        /**
+         * Tells the manager that the VibrationThread is finished with the previous vibration and
+         * all of its cleanup tasks, and the vibrators can now be used for another vibration.
+         */
+        void onVibrationThreadReleased();
     }
 
     private final Object mLock = new Object();
@@ -105,7 +115,7 @@
     private final VibrationSettings mVibrationSettings;
     private final DeviceVibrationEffectAdapter mDeviceEffectAdapter;
     private final Vibration mVibration;
-    private final VibrationCallbacks mCallbacks;
+    private final VibratorManagerHooks mVibratorManagerHooks;
     private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
     private final StepQueue mStepQueue = new StepQueue();
 
@@ -117,11 +127,11 @@
     VibrationThread(Vibration vib, VibrationSettings vibrationSettings,
             DeviceVibrationEffectAdapter effectAdapter,
             SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock,
-            IBatteryStats batteryStatsService, VibrationCallbacks callbacks) {
+            IBatteryStats batteryStatsService, VibratorManagerHooks vibratorManagerHooks) {
         mVibration = vib;
         mVibrationSettings = vibrationSettings;
         mDeviceEffectAdapter = effectAdapter;
-        mCallbacks = callbacks;
+        mVibratorManagerHooks = vibratorManagerHooks;
         mWorkSource = new WorkSource(mVibration.uid);
         mWakeLock = wakeLock;
         mBatteryStatsService = batteryStatsService;
@@ -163,7 +173,7 @@
                 clientVibrationCompleteIfNotAlready(Vibration.Status.FINISHED_UNEXPECTED);
             }
         } finally {
-            mCallbacks.onVibratorsReleased();
+            mVibratorManagerHooks.onVibrationThreadReleased();
         }
     }
 
@@ -263,7 +273,7 @@
     private void clientVibrationCompleteIfNotAlready(Vibration.Status completedStatus) {
         if (!mCalledVibrationCompleteCallback) {
             mCalledVibrationCompleteCallback = true;
-            mCallbacks.onVibrationCompleted(mVibration.id, completedStatus);
+            mVibratorManagerHooks.onVibrationCompleted(mVibration.id, completedStatus);
         }
     }
 
@@ -272,25 +282,29 @@
         try {
             CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect());
             final int sequentialEffectSize = sequentialEffect.getEffects().size();
-            mStepQueue.offer(new StartVibrateStep(sequentialEffect));
+            mStepQueue.initializeForEffect(sequentialEffect);
 
-            while (!mStepQueue.isEmpty()) {
-                long waitTime;
+            while (!mStepQueue.isFinished()) {
+                long waitMillisBeforeNextStep;
                 synchronized (mLock) {
-                    waitTime = mStepQueue.calculateWaitTime();
-                    if (waitTime > 0) {
+                    waitMillisBeforeNextStep = mStepQueue.getWaitMillisBeforeNextStep();
+                    if (waitMillisBeforeNextStep > 0) {
                         try {
-                            mLock.wait(waitTime);
+                            mLock.wait(waitMillisBeforeNextStep);
                         } catch (InterruptedException e) {
                         }
                     }
                 }
-                // If we waited, the queue may have changed, so let the loop run again.
-                if (waitTime <= 0) {
+                // Only run the next vibration step if we didn't have to wait in this loop.
+                // If we waited then the queue may have changed, so loop again to re-evaluate
+                // the scheduling of the queue top element.
+                if (waitMillisBeforeNextStep <= 0) {
                     if (DEBUG) {
                         Slog.d(TAG, "Play vibration consuming next step...");
                     }
-                    mStepQueue.consumeNext();
+                    // Run the step without holding the main lock, to avoid HAL interactions from
+                    // blocking the thread.
+                    mStepQueue.runNextStep();
                 }
                 Vibration.Status status = mStop ? Vibration.Status.CANCELLED
                         : mStepQueue.calculateVibrationStatus(sequentialEffectSize);
@@ -350,7 +364,7 @@
         }
         if (segmentIndex < 0) {
             // No more segments to play, last step is to complete the vibration on this vibrator.
-            return new CompleteStep(startTime, /* cancelled= */ false, controller,
+            return new EffectCompleteStep(startTime, /* cancelled= */ false, controller,
                     vibratorOffTimeout);
         }
 
@@ -385,7 +399,7 @@
         @GuardedBy("mLock")
         private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
         @GuardedBy("mLock")
-        private final Queue<Integer> mNotifiedVibrators = new LinkedList<>();
+        private final Queue<Integer> mCompletionNotifiedVibrators = new LinkedList<>();
 
         @GuardedBy("mLock")
         private int mPendingVibrateSteps;
@@ -394,18 +408,16 @@
         @GuardedBy("mLock")
         private int mSuccessfulVibratorOnSteps;
         @GuardedBy("mLock")
-        private boolean mWaitToProcessVibratorCallbacks;
+        private boolean mWaitToProcessVibratorCompleteCallbacks;
 
-        public void offer(@NonNull Step step) {
+        public void initializeForEffect(@NonNull CombinedVibration.Sequential vibration) {
             synchronized (mLock) {
-                if (!step.isCleanUp()) {
-                    mPendingVibrateSteps++;
-                }
-                mNextSteps.offer(step);
+                mPendingVibrateSteps++;
+                mNextSteps.offer(new StartVibrateStep(vibration));
             }
         }
 
-        public boolean isEmpty() {
+        public boolean isFinished() {
             synchronized (mLock) {
                 return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
             }
@@ -429,11 +441,11 @@
             }
         }
 
-        /** Returns the time in millis to wait before calling {@link #consumeNext()}. */
-        @GuardedBy("mLock")
-        public long calculateWaitTime() {
+        /** Returns the time in millis to wait before calling {@link #runNextStep()}. */
+        @GuardedBy("VibrationThread.this.mLock")
+        public long getWaitMillisBeforeNextStep() {
             if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
-                // Steps anticipated by vibrator complete callback should be played right away.
+                // Steps resumed by vibrator complete callback should be played right away.
                 return 0;
             }
             Step nextStep = mNextSteps.peek();
@@ -444,7 +456,7 @@
          * Play and remove the step at the top of this queue, and also adds the next steps generated
          * to be played next.
          */
-        public void consumeNext() {
+        public void runNextStep() {
             // Vibrator callbacks should wait until the polled step is played and the next steps are
             // added back to the queue, so they can handle the callback.
             markWaitToProcessVibratorCallbacks();
@@ -472,7 +484,7 @@
                 }
             } finally {
                 synchronized (mLock) {
-                    processVibratorCallbacks();
+                    processVibratorCompleteCallbacks();
                 }
             }
         }
@@ -485,10 +497,10 @@
          */
         @GuardedBy("mLock")
         public void notifyVibratorComplete(int vibratorId) {
-            mNotifiedVibrators.offer(vibratorId);
-            if (!mWaitToProcessVibratorCallbacks) {
+            mCompletionNotifiedVibrators.offer(vibratorId);
+            if (!mWaitToProcessVibratorCompleteCallbacks) {
                 // No step is being played or cancelled now, process the callback right away.
-                processVibratorCallbacks();
+                processVibratorCompleteCallbacks();
             }
         }
 
@@ -515,7 +527,7 @@
                 }
             } finally {
                 synchronized (mLock) {
-                    processVibratorCallbacks();
+                    processVibratorCompleteCallbacks();
                 }
             }
         }
@@ -540,7 +552,7 @@
                 }
             } finally {
                 synchronized (mLock) {
-                    processVibratorCallbacks();
+                    processVibratorCompleteCallbacks();
                 }
             }
         }
@@ -548,7 +560,7 @@
         @Nullable
         private Step pollNext() {
             synchronized (mLock) {
-                // Prioritize the steps anticipated by a vibrator complete callback.
+                // Prioritize the steps resumed by a vibrator complete callback.
                 if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
                     return mPendingOnVibratorCompleteSteps.poll();
                 }
@@ -558,29 +570,29 @@
 
         private void markWaitToProcessVibratorCallbacks() {
             synchronized (mLock) {
-                mWaitToProcessVibratorCallbacks = true;
+                mWaitToProcessVibratorCompleteCallbacks = true;
             }
         }
 
         /**
-         * Notify the step in this queue that should be anticipated by the vibrator completion
-         * callback and keep it separate to be consumed by {@link #consumeNext()}.
+         * Notify the step in this queue that should be resumed by the vibrator completion
+         * callback and keep it separate to be consumed by {@link #runNextStep()}.
          *
          * <p>This is a lightweight method that do not trigger any operation from {@link
          * VibratorController}, so it can be called directly from a native callback.
          *
          * <p>This assumes only one of the next steps is waiting on this given vibrator, so the
-         * first step found will be anticipated by this method, in no particular order.
+         * first step found will be resumed by this method, in no particular order.
          */
         @GuardedBy("mLock")
-        private void processVibratorCallbacks() {
-            mWaitToProcessVibratorCallbacks = false;
-            while (!mNotifiedVibrators.isEmpty()) {
-                int vibratorId = mNotifiedVibrators.poll();
+        private void processVibratorCompleteCallbacks() {
+            mWaitToProcessVibratorCompleteCallbacks = false;
+            while (!mCompletionNotifiedVibrators.isEmpty()) {
+                int vibratorId = mCompletionNotifiedVibrators.poll();
                 Iterator<Step> it = mNextSteps.iterator();
                 while (it.hasNext()) {
                     Step step = it.next();
-                    if (step.shouldPlayWhenVibratorComplete(vibratorId)) {
+                    if (step.acceptVibratorCompleteCallback(vibratorId)) {
                         it.remove();
                         mPendingOnVibratorCompleteSteps.offer(step);
                         break;
@@ -637,10 +649,10 @@
         }
 
         /**
-         * Return true to play this step right after a vibrator has notified vibration completed,
-         * used to anticipate steps waiting on vibrator callbacks with a timeout.
+         * Return true to run this step right after a vibrator has notified vibration completed,
+         * used to resume steps waiting on vibrator callbacks with a timeout.
          */
-        public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+        public boolean acceptVibratorCompleteCallback(int vibratorId) {
             return false;
         }
 
@@ -670,7 +682,7 @@
      * add a {@link FinishVibrateStep} to the queue, to be played after all vibrators have finished
      * all their individual steps.
      *
-     * <o>If this step does not start any vibrator, it will add a {@link StartVibrateStep} if the
+     * <p>If this step does not start any vibrator, it will add a {@link StartVibrateStep} if the
      * sequential effect isn't finished yet.
      */
     private final class StartVibrateStep extends Step {
@@ -805,7 +817,7 @@
                 boolean hasTriggered = false;
                 long maxDuration = 0;
                 try {
-                    hasPrepared = mCallbacks.prepareSyncedVibration(
+                    hasPrepared = mVibratorManagerHooks.prepareSyncedVibration(
                             effectMapping.getRequiredSyncCapabilities(),
                             effectMapping.getVibratorIds());
 
@@ -821,13 +833,13 @@
                     // Check if sync was prepared and if any step was accepted by a vibrator,
                     // otherwise there is nothing to trigger here.
                     if (hasPrepared && maxDuration > 0) {
-                        hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id);
+                        hasTriggered = mVibratorManagerHooks.triggerSyncedVibration(mVibration.id);
                     }
                     return maxDuration;
                 } finally {
                     if (hasPrepared && !hasTriggered) {
                         // Trigger has failed or all steps were ignored by the vibrators.
-                        mCallbacks.cancelSyncedVibration();
+                        mVibratorManagerHooks.cancelSyncedVibration();
                         nextSteps.clear();
                     } else if (maxDuration < 0) {
                         // Some vibrator failed without being prepared so other vibrators might be
@@ -910,7 +922,7 @@
         public final long vibratorOffTimeout;
 
         long mVibratorOnResult;
-        boolean mVibratorCallbackReceived;
+        boolean mVibratorCompleteCallbackReceived;
 
         /**
          * @param startTime          The time to schedule this step in the {@link StepQueue}.
@@ -919,7 +931,7 @@
          * @param index              The index of the next segment to be played by this step
          * @param vibratorOffTimeout The time the vibrator is expected to complete any previous
          *                           vibration and turn off. This is used to allow this step to be
-         *                           anticipated when the completion callback is triggered, and can
+         *                           triggered when the completion callback is received, and can
          *                           be used play effects back-to-back.
          */
         SingleVibratorStep(long startTime, VibratorController controller,
@@ -937,17 +949,17 @@
         }
 
         @Override
-        public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+        public boolean acceptVibratorCompleteCallback(int vibratorId) {
             boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
-            mVibratorCallbackReceived |= isSameVibrator;
-            // Only anticipate this step if a timeout was set to wait for the vibration to complete,
+            mVibratorCompleteCallbackReceived |= isSameVibrator;
+            // Only activate this step if a timeout was set to wait for the vibration to complete,
             // otherwise we are waiting for the correct time to play the next step.
             return isSameVibrator && (vibratorOffTimeout > SystemClock.uptimeMillis());
         }
 
         @Override
         public List<Step> cancel() {
-            return Arrays.asList(new CompleteStep(SystemClock.uptimeMillis(),
+            return Arrays.asList(new EffectCompleteStep(SystemClock.uptimeMillis(),
                     /* cancelled= */ true, controller, vibratorOffTimeout));
         }
 
@@ -1205,10 +1217,10 @@
      * <p>This runs right at the time the vibration is considered to end and will update the pending
      * vibrators count. This can turn off the vibrator or slowly ramp it down to zero amplitude.
      */
-    private final class CompleteStep extends SingleVibratorStep {
+    private final class EffectCompleteStep extends SingleVibratorStep {
         private final boolean mCancelled;
 
-        CompleteStep(long startTime, boolean cancelled, VibratorController controller,
+        EffectCompleteStep(long startTime, boolean cancelled, VibratorController controller,
                 long vibratorOffTimeout) {
             super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
             mCancelled = cancelled;
@@ -1232,13 +1244,13 @@
 
         @Override
         public List<Step> play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteStep");
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "EffectCompleteStep");
             try {
                 if (DEBUG) {
                     Slog.d(TAG, "Running " + (mCancelled ? "cancel" : "complete") + " vibration"
                             + " step on vibrator " + controller.getVibratorInfo().getId());
                 }
-                if (mVibratorCallbackReceived) {
+                if (mVibratorCompleteCallbackReceived) {
                     // Vibration completion callback was received by this step, just turn if off
                     // and skip any clean-up.
                     stopVibrating();
@@ -1310,7 +1322,7 @@
                     Slog.d(TAG, "Ramp down the vibrator amplitude, step with "
                             + latency + "ms latency.");
                 }
-                if (mVibratorCallbackReceived) {
+                if (mVibratorCompleteCallbackReceived) {
                     // Vibration completion callback was received by this step, just turn if off
                     // and skip the rest of the steps to ramp down the vibrator amplitude.
                     stopVibrating();
@@ -1337,7 +1349,7 @@
      * Represents a step to turn the vibrator off.
      *
      * <p>This runs after a timeout on the expected time the vibrator should have finished playing,
-     * and can anticipated by vibrator complete callbacks.
+     * and can be brought forward by vibrator complete callbacks.
      */
     private final class OffStep extends SingleVibratorStep {
 
@@ -1389,13 +1401,14 @@
         }
 
         @Override
-        public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+        public boolean acceptVibratorCompleteCallback(int vibratorId) {
             if (controller.getVibratorInfo().getId() == vibratorId) {
-                mVibratorCallbackReceived = true;
+                mVibratorCompleteCallbackReceived = true;
                 mNextOffTime = SystemClock.uptimeMillis();
             }
-            // Timings are tightly controlled here, so only anticipate if the vibrator was supposed
-            // to be ON but has completed prematurely, to turn it back on as soon as possible.
+            // Timings are tightly controlled here, so only trigger this step if the vibrator was
+            // supposed to be ON but has completed prematurely, to turn it back on as soon as
+            // possible.
             return mNextOffTime < startTime && controller.getCurrentAmplitude() > 0;
         }
 
@@ -1409,8 +1422,8 @@
                     Slog.d(TAG, "Running amplitude step with " + latency + "ms latency.");
                 }
 
-                if (mVibratorCallbackReceived && latency < 0) {
-                    // This step was anticipated because the vibrator turned off prematurely.
+                if (mVibratorCompleteCallbackReceived && latency < 0) {
+                    // This step was run early because the vibrator turned off prematurely.
                     // Turn it back on and return this same step to run at the exact right time.
                     mNextOffTime = turnVibratorBackOn(/* remainingDuration= */ -latency);
                     return Arrays.asList(new AmplitudeStep(startTime, controller, effect,
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index b2e34da..63f3af3 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -125,7 +125,8 @@
     private final long mCapabilities;
     private final int[] mVibratorIds;
     private final SparseArray<VibratorController> mVibrators;
-    private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks();
+    private final VibrationThreadCallbacks mVibrationThreadCallbacks =
+            new VibrationThreadCallbacks();
     @GuardedBy("mLock")
     private final SparseArray<AlwaysOnVibration> mAlwaysOnEffects = new SparseArray<>();
     @GuardedBy("mLock")
@@ -634,7 +635,7 @@
 
             VibrationThread vibThread = new VibrationThread(vib, mVibrationSettings,
                     mDeviceVibrationEffectAdapter, mVibrators, mWakeLock, mBatteryStatsService,
-                    mVibrationCallbacks);
+                    mVibrationThreadCallbacks);
 
             if (mCurrentVibration == null) {
                 return startVibrationThreadLocked(vibThread);
@@ -1115,10 +1116,10 @@
     }
 
     /**
-     * Implementation of {@link VibrationThread.VibrationCallbacks} that controls synced vibrations
-     * and reports them when finished.
+     * Implementation of {@link VibrationThread.VibratorManagerHooks} that controls synced
+     * vibrations and reports them when finished.
      */
-    private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {
+    private final class VibrationThreadCallbacks implements VibrationThread.VibratorManagerHooks {
 
         @Override
         public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
@@ -1153,7 +1154,7 @@
         }
 
         @Override
-        public void onVibratorsReleased() {
+        public void onVibrationThreadReleased() {
             if (DEBUG) {
                 Slog.d(TAG, "Vibrators released after finished vibration");
             }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 87ba859..b0efa5b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -628,6 +628,10 @@
     // it references to gets removed. This should also be cleared when we move out of pip.
     private Task mLastParentBeforePip;
 
+    // Only set if this instance is a launch-into-pip Activity, points to the
+    // host Activity the launch-into-pip Activity is originated from.
+    private ActivityRecord mLaunchIntoPipHostActivity;
+
     boolean firstWindowDrawn;
     /** Whether the visible window(s) of this activity is drawn. */
     private boolean mReportedDrawn;
@@ -1225,6 +1229,9 @@
         if (mLastParentBeforePip != null) {
             pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
         }
+        if (mLaunchIntoPipHostActivity != null) {
+            pw.println(prefix + "launchIntoPipHostActivity=" + mLaunchIntoPipHostActivity);
+        }
 
         mLetterboxUiController.dump(pw, prefix);
 
@@ -1559,10 +1566,16 @@
     /**
      * Sets {@link #mLastParentBeforePip} to the current parent Task, it's caller's job to ensure
      * {@link #getTask()} is set before this is called.
+     *
+     * @param launchIntoPipHostActivity {@link ActivityRecord} as the host Activity for the
+     *        launch-int-pip Activity see also {@link #mLaunchIntoPipHostActivity}.
      */
-    void setLastParentBeforePip() {
-        mLastParentBeforePip = getTask();
+    void setLastParentBeforePip(@Nullable ActivityRecord launchIntoPipHostActivity) {
+        mLastParentBeforePip = (launchIntoPipHostActivity == null)
+                ? getTask()
+                : launchIntoPipHostActivity.getTask();
         mLastParentBeforePip.mChildPipActivity = this;
+        mLaunchIntoPipHostActivity = launchIntoPipHostActivity;
     }
 
     private void clearLastParentBeforePip() {
@@ -1570,12 +1583,17 @@
             mLastParentBeforePip.mChildPipActivity = null;
             mLastParentBeforePip = null;
         }
+        mLaunchIntoPipHostActivity = null;
     }
 
     @Nullable Task getLastParentBeforePip() {
         return mLastParentBeforePip;
     }
 
+    @Nullable ActivityRecord getLaunchIntoPipHostActivity() {
+        return mLaunchIntoPipHostActivity;
+    }
+
     private void updateColorTransform() {
         if (mSurfaceControl != null && mLastAppSaturationInfo != null) {
             getPendingTransaction().setColorTransform(mSurfaceControl,
@@ -1856,6 +1874,10 @@
                 mRotationAnimationHint = rotationAnimation;
             }
 
+            if (options.getLaunchIntoPipParams() != null) {
+                pictureInPictureArgs = options.getLaunchIntoPipParams();
+            }
+
             mOverrideTaskTransition = options.getOverrideTaskTransition();
         }
 
@@ -2515,7 +2537,6 @@
         }
         removeStartingWindowAnimation(true /* prepareAnimation */);
 
-        // TODO(b/215316431): Add tests
         final Task task = getTask();
         if (prevEligibleForLetterboxEducation != isEligibleForLetterboxEducation()
                 && task != null) {
@@ -7695,7 +7716,6 @@
      *     once the starting window is removed in {@link #removeStartingWindow}).
      * </ul>
      */
-    // TODO(b/215316431): Add tests
     boolean isEligibleForLetterboxEducation() {
         return mWmService.mLetterboxConfiguration.getIsEducationEnabled()
                 && mIsEligibleForFixedOrientationLetterbox
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5ffe214..ef0ee12 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1896,6 +1896,14 @@
         mSupervisor.handleNonResizableTaskIfNeeded(startedTask,
                 mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetRootTask);
 
+        // If Activity's launching into PiP, move the mStartActivity immediately to pinned mode.
+        // Note that mStartActivity and source should be in the same Task at this point.
+        if (mOptions != null && mOptions.isLaunchIntoPip()
+                && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()) {
+            mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity,
+                    sourceRecord, "launch-into-pip");
+        }
+
         return START_SUCCESS;
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 1741d0f..0497477 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3538,8 +3538,8 @@
                 final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
                 final float expandedAspectRatio = r.pictureInPictureArgs.getExpandedAspectRatio();
                 final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
-                mRootWindowContainer.moveActivityToPinnedRootTask(
-                        r, "enterPictureInPictureMode");
+                mRootWindowContainer.moveActivityToPinnedRootTask(r,
+                        null /* launchIntoPipHostActivity */, "enterPictureInPictureMode");
                 final Task task = r.getTask();
                 task.setPictureInPictureAspectRatio(aspectRatio, expandedAspectRatio);
                 task.setPictureInPictureActions(actions);
@@ -3922,11 +3922,11 @@
     @Override
     public void onPictureInPictureStateChanged(PictureInPictureUiState pipState) {
         enforceTaskPermission("onPictureInPictureStateChanged");
-        final Task rootPinnedStask = mRootWindowContainer.getDefaultTaskDisplayArea()
+        final Task rootPinnedTask = mRootWindowContainer.getDefaultTaskDisplayArea()
                 .getRootPinnedTask();
-        if (rootPinnedStask != null && rootPinnedStask.getTopMostActivity() != null) {
+        if (rootPinnedTask != null && rootPinnedTask.getTopMostActivity() != null) {
             mWindowManager.mAtmService.mActivityClientController.onPictureInPictureStateChanged(
-                    rootPinnedStask.getTopMostActivity(), pipState);
+                    rootPinnedTask.getTopMostActivity(), pipState);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 132396b..0868111 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -652,7 +652,9 @@
         void prepareSurfaces() {
             mDimmer.resetDimStates();
             super.prepareSurfaces();
+            // Bounds need to be relative, as the dim layer is a child.
             getBounds(mTmpDimBoundsRect);
+            mTmpDimBoundsRect.offsetTo(0 /* newLeft */, 0 /* newTop */);
 
             // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer
             // on the display level fades out.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2a06d8b..ddfdddc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -457,8 +457,6 @@
      */
     private boolean mLastWallpaperVisible = false;
 
-    private Rect mBaseDisplayRect = new Rect();
-
     // Accessed directly by all users.
     private boolean mLayoutNeeded;
     int pendingLayoutChanges;
@@ -488,9 +486,6 @@
     private final Rect mTmpRect2 = new Rect();
     private final Region mTmpRegion = new Region();
 
-    /** Used for handing back size of display */
-    private final Rect mTmpBounds = new Rect();
-
     private final Configuration mTmpConfiguration = new Configuration();
 
     /** Remove this display when animation on it has completed. */
@@ -1362,8 +1357,8 @@
         return mDisplayRotation;
     }
 
-    void setInsetProvider(@InternalInsetsType int type, WindowState win,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider){
+    void setInsetProvider(@InternalInsetsType int type, WindowContainer win,
+            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider) {
         setInsetProvider(type, win, frameProvider, null /* imeFrameProvider */);
     }
 
@@ -1377,10 +1372,10 @@
      * @param imeFrameProvider Function to compute the frame when dispatching insets to the IME, or
      *                         {@code null} if the normal frame should be taken.
      */
-    void setInsetProvider(@InternalInsetsType int type, WindowState win,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider) {
-        mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider,
+    void setInsetProvider(@InternalInsetsType int type, WindowContainer win,
+            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider,
+            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider) {
+        mInsetsStateController.getSourceProvider(type).setWindowContainer(win, frameProvider,
                 imeFrameProvider);
     }
 
@@ -2080,8 +2075,6 @@
         mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
                 mDisplayInfo);
 
-        mBaseDisplayRect.set(0, 0, dw, dh);
-
         if (isDefaultDisplay) {
             mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics,
                     mCompatDisplayMetrics);
@@ -2213,14 +2206,14 @@
      */
     void computeScreenConfiguration(Configuration config) {
         final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode, config);
-        calculateBounds(displayInfo, mTmpBounds);
-        config.windowConfiguration.setBounds(mTmpBounds);
-        config.windowConfiguration.setMaxBounds(mTmpBounds);
+        final int dw = displayInfo.logicalWidth;
+        final int dh = displayInfo.logicalHeight;
+        mTmpRect.set(0, 0, dw, dh);
+        config.windowConfiguration.setBounds(mTmpRect);
+        config.windowConfiguration.setMaxBounds(mTmpRect);
         config.windowConfiguration.setWindowingMode(getWindowingMode());
         config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
 
-        final int dw = displayInfo.logicalWidth;
-        final int dh = displayInfo.logicalHeight;
         computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation, config.uiMode,
                 displayInfo.displayCutout);
 
@@ -2844,10 +2837,6 @@
 
     /** Update base (override) display metrics. */
     void updateBaseDisplayMetrics(int baseWidth, int baseHeight, int baseDensity) {
-        final int originalWidth = mBaseDisplayWidth;
-        final int originalHeight = mBaseDisplayHeight;
-        final int originalDensity = mBaseDisplayDensity;
-
         mBaseDisplayWidth = baseWidth;
         mBaseDisplayHeight = baseHeight;
         mBaseDisplayDensity = baseDensity;
@@ -2867,12 +2856,6 @@
                         + mBaseDisplayHeight + " on display:" + getDisplayId());
             }
         }
-
-        if (mBaseDisplayWidth != originalWidth || mBaseDisplayHeight != originalHeight
-                || mBaseDisplayDensity != originalDensity) {
-            mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
-            updateBounds();
-        }
     }
 
     /**
@@ -3021,7 +3004,7 @@
         if (focusedTask == null) {
             mTouchExcludeRegion.setEmpty();
         } else {
-            mTouchExcludeRegion.set(mBaseDisplayRect);
+            mTouchExcludeRegion.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
             final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
             mTmpRect.setEmpty();
             mTmpRect2.setEmpty();
@@ -3822,7 +3805,7 @@
             final int imePid = mInputMethodWindow.mSession.mPid;
             mAtmService.onImeWindowSetOnDisplayArea(imePid, mImeWindowsContainer);
         }
-        mInsetsStateController.getSourceProvider(ITYPE_IME).setWindow(win,
+        mInsetsStateController.getSourceProvider(ITYPE_IME).setWindowContainer(win,
                 mDisplayPolicy.getImeSourceFrameProvider(), null /* imeFrameProvider */);
         computeImeTarget(true /* updateImeTarget */);
         updateImeControlTarget();
@@ -4587,25 +4570,6 @@
         }
     }
 
-    private void updateBounds() {
-        calculateBounds(mDisplayInfo, mTmpBounds);
-        setBounds(mTmpBounds);
-    }
-
-    // Determines the current display bounds based on the current state
-    private void calculateBounds(DisplayInfo displayInfo, Rect out) {
-        // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
-        final int rotation = displayInfo.rotation;
-        boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        final int physWidth = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
-        final int physHeight = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
-        int width = displayInfo.logicalWidth;
-        int left = (physWidth - width) / 2;
-        int height = displayInfo.logicalHeight;
-        int top = (physHeight - height) / 2;
-        out.set(left, top, left + width, top + height);
-    }
-
     private void getBounds(Rect out, @Rotation int rotation) {
         getBounds(out);
 
@@ -5561,8 +5525,6 @@
     }
 
     void onDisplayChanged() {
-        mDisplay.getRealSize(mTmpDisplaySize);
-        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
         final int lastDisplayState = mDisplayInfo.state;
         updateDisplayInfo();
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 30fffd3..4148d8b 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1106,8 +1106,8 @@
                 break;
             case TYPE_STATUS_BAR:
                 mStatusBar = win;
-                final TriConsumer<DisplayFrames, WindowState, Rect> gestureFrameProvider =
-                        (displayFrames, windowState, rect) -> {
+                final TriConsumer<DisplayFrames, WindowContainer, Rect> gestureFrameProvider =
+                        (displayFrames, windowContainer, rect) -> {
                             rect.bottom = rect.top + getStatusBarHeight(displayFrames);
                             final DisplayCutout cutout =
                                     displayFrames.mInsetsState.getDisplayCutout();
@@ -1128,24 +1128,25 @@
             case TYPE_NAVIGATION_BAR:
                 mNavigationBar = win;
                 mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
-                        (displayFrames, windowState, inOutFrame) -> {
+                        (displayFrames, windowContainer, inOutFrame) -> {
                             if (!mNavButtonForcedVisible) {
-                                inOutFrame.inset(windowState.getLayoutingAttrs(
+                                inOutFrame.inset(win.getLayoutingAttrs(
                                         displayFrames.mRotation).providedInternalInsets);
                                 inOutFrame.inset(win.mGivenContentInsets);
                             }
                         },
 
                         // For IME we use regular frame.
-                        (displayFrames, windowState, inOutFrame) ->
-                                inOutFrame.set(windowState.getFrame()));
+                        (displayFrames, windowContainer, inOutFrame) -> {
+                            inOutFrame.set(win.getFrame());
+                        });
 
                 mDisplayContent.setInsetProvider(ITYPE_BOTTOM_MANDATORY_GESTURES, win,
-                        (displayFrames, windowState, inOutFrame) -> {
+                        (displayFrames, windowContainer, inOutFrame) -> {
                             inOutFrame.top -= mBottomGestureAdditionalInset;
                         });
                 mDisplayContent.setInsetProvider(ITYPE_LEFT_GESTURES, win,
-                        (displayFrames, windowState, inOutFrame) -> {
+                        (displayFrames, windowContainer, inOutFrame) -> {
                             final int leftSafeInset =
                                     Math.max(displayFrames.mDisplayCutoutSafe.left, 0);
                             inOutFrame.left = 0;
@@ -1154,7 +1155,7 @@
                             inOutFrame.right = leftSafeInset + mLeftGestureInset;
                         });
                 mDisplayContent.setInsetProvider(ITYPE_RIGHT_GESTURES, win,
-                        (displayFrames, windowState, inOutFrame) -> {
+                        (displayFrames, windowContainer, inOutFrame) -> {
                             final int rightSafeInset =
                                     Math.min(displayFrames.mDisplayCutoutSafe.right,
                                             displayFrames.mUnrestricted.right);
@@ -1164,8 +1165,8 @@
                             inOutFrame.right = displayFrames.mDisplayWidth;
                         });
                 mDisplayContent.setInsetProvider(ITYPE_BOTTOM_TAPPABLE_ELEMENT, win,
-                        (displayFrames, windowState, inOutFrame) -> {
-                            if ((windowState.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0
+                        (displayFrames, windowContainer, inOutFrame) -> {
+                            if ((win.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0
                                     || mNavigationBarLetsThroughTaps) {
                                 inOutFrame.setEmpty();
                             }
@@ -1176,11 +1177,13 @@
             default:
                 if (attrs.providesInsetsTypes != null) {
                     for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
-                        final TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider =
+                        final TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider =
                                 !attrs.providedInternalImeInsets.equals(Insets.NONE)
-                                        ? (displayFrames, windowState, inOutFrame) ->
-                                        inOutFrame.inset(windowState.getLayoutingAttrs(
-                                                displayFrames.mRotation).providedInternalImeInsets)
+                                        ? (displayFrames, windowContainer, inOutFrame) -> {
+                                            inOutFrame.inset(win.getLayoutingAttrs(
+                                                    displayFrames.mRotation)
+                                                    .providedInternalImeInsets);
+                                        }
                                         : null;
                         switch (insetsType) {
                             case ITYPE_STATUS_BAR:
@@ -1201,10 +1204,9 @@
                                 break;
                         }
                         mDisplayContent.setInsetProvider(insetsType, win, (displayFrames,
-                                windowState, inOutFrame) -> {
-                            inOutFrame.inset(
-                                    windowState.getLayoutingAttrs(displayFrames.mRotation)
-                                            .providedInternalInsets);
+                                windowContainer, inOutFrame) -> {
+                            inOutFrame.inset(win.getLayoutingAttrs(
+                                    displayFrames.mRotation).providedInternalInsets);
                             inOutFrame.inset(win.mGivenContentInsets);
                         }, imeFrameProvider);
                         mInsetsSourceWindowsExceptIme.add(win);
@@ -1230,8 +1232,13 @@
         }
     }
 
-    TriConsumer<DisplayFrames, WindowState, Rect> getImeSourceFrameProvider() {
-        return (displayFrames, windowState, inOutFrame) -> {
+    TriConsumer<DisplayFrames, WindowContainer, Rect> getImeSourceFrameProvider() {
+        return (displayFrames, windowContainer, inOutFrame) -> {
+            WindowState windowState = windowContainer.asWindowState();
+            if (windowState == null) {
+                throw new IllegalArgumentException("IME insets must be provided by a window.");
+            }
+
             if (mNavigationBar != null && navigationBarPosition(displayFrames.mRotation)
                     == NAV_BAR_BOTTOM) {
                 // In gesture navigation, nav bar frame is larger than frame to calculate insets.
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index cbefe7f..8f97220 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -113,8 +113,8 @@
     private void reportImeDrawnForOrganizer(InsetsControlTarget caller) {
         if (caller.getWindow() != null && caller.getWindow().getTask() != null) {
             if (caller.getWindow().getTask().isOrganized()) {
-                mWin.mWmService.mAtmService.mTaskOrganizerController.reportImeDrawnOnTask(
-                        caller.getWindow().getTask());
+                mWindowContainer.mWmService.mAtmService.mTaskOrganizerController
+                        .reportImeDrawnOnTask(caller.getWindow().getTask());
             }
         }
     }
@@ -173,12 +173,18 @@
     }
 
     void checkShowImePostLayout() {
+        if (mWindowContainer == null) {
+            return;
+        }
+        WindowState windowState =  mWindowContainer.asWindowState();
+        if (windowState == null) {
+            throw new IllegalArgumentException("IME insets must be provided by a window.");
+        }
         // check if IME is drawn
         if (mIsImeLayoutDrawn
                 || (isReadyToShowIme()
-                && mWin != null
-                && mWin.isDrawn()
-                && !mWin.mGivenInsetsPending)) {
+                && windowState.isDrawn()
+                && !windowState.mGivenInsetsPending)) {
             mIsImeLayoutDrawn = true;
             // show IME if InputMethodService requested it to be shown.
             if (mShowImeRunner != null) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index d28dfd5..433f1071 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -19,24 +19,36 @@
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.ITYPE_INVALID;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityTaskManager;
 import android.app.StatusBarManager;
+import android.app.WindowConfiguration;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.util.ArrayMap;
 import android.util.IntArray;
 import android.util.SparseArray;
 import android.view.InsetsAnimationControlCallbacks;
@@ -164,8 +176,9 @@
     }
 
     boolean isHidden(@InternalInsetsType int type) {
-        final InsetsSourceProvider provider = mStateController.peekSourceProvider(type);
-        return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
+        final InsetsSourceProvider provider =  mStateController.peekSourceProvider(type);
+        return provider != null && provider.hasWindowContainer()
+                && !provider.getSource().isVisible();
     }
 
     void showTransient(@InternalInsetsType int[] types, boolean isGestureOnSystemBar) {
@@ -235,10 +248,11 @@
     }
 
     /**
-     * @see InsetsStateController#getInsetsForWindow
+     * Adjusts the sources in {@code originalState} to account for things like transient bars, IME
+     * & rounded corners.
      */
-    InsetsState getInsetsForWindow(WindowState target, boolean includesTransient) {
-        final InsetsState originalState = mStateController.getInsetsForWindow(target);
+    InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState,
+            boolean includesTransient) {
         InsetsState state;
         if (!includesTransient) {
             state = adjustVisibilityForTransientTypes(originalState);
@@ -249,17 +263,131 @@
         return adjustInsetsForRoundedCorners(target, state, state == originalState);
     }
 
-    InsetsState getInsetsForWindow(WindowState target) {
-        return getInsetsForWindow(target, false);
+    InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState) {
+        return adjustInsetsForWindow(target, originalState, false);
+    }
+
+    /**
+     * @see WindowState#getInsetsState()
+     */
+    InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
+        final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs);
+        final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
+        if (token != null) {
+            final InsetsState rotatedState = token.getFixedRotationTransformInsetsState();
+            if (rotatedState != null) {
+                return rotatedState;
+            }
+        }
+        final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
+        // Always use windowing mode fullscreen when get insets for window metrics to make sure it
+        // contains all insets types.
+        final InsetsState originalState = mDisplayContent.getInsetsPolicy()
+                .enforceInsetsPolicyForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop,
+                        mStateController.getRawInsetsState());
+        return adjustVisibilityForTransientTypes(originalState);
+    }
+
+    private static @InternalInsetsType int getInsetsTypeForLayoutParams(
+            WindowManager.LayoutParams attrs) {
+        @WindowManager.LayoutParams.WindowType int type = attrs.type;
+        switch (type) {
+            case TYPE_STATUS_BAR:
+                return ITYPE_STATUS_BAR;
+            case TYPE_NAVIGATION_BAR:
+                return ITYPE_NAVIGATION_BAR;
+            case TYPE_INPUT_METHOD:
+                return ITYPE_IME;
+        }
+
+        // If not one of the types above, check whether an internal inset mapping is specified.
+        if (attrs.providesInsetsTypes != null) {
+            for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
+                switch (insetsType) {
+                    case ITYPE_STATUS_BAR:
+                    case ITYPE_NAVIGATION_BAR:
+                    case ITYPE_CLIMATE_BAR:
+                    case ITYPE_EXTRA_NAVIGATION_BAR:
+                        return insetsType;
+                }
+            }
+        }
+
+        return ITYPE_INVALID;
     }
 
 
     /**
-     * @see InsetsStateController#getInsetsForWindowMetrics
+     * Modifies the given {@code state} according to the {@code type} (Inset type) provided by
+     * the target.
+     * When performing layout of the target or dispatching insets to the target, we need to exclude
+     * sources which should not be visible to the target. e.g., the source which represents the
+     * target window itself, and the IME source when the target is above IME. We also need to
+     * exclude certain types of insets source for client within specific windowing modes.
+     *
+     * @param type the inset type provided by the target
+     * @param windowingMode the windowing mode of the target
+     * @param isAlwaysOnTop is the target always on top
+     * @param state the input inset state containing all the sources
+     * @return The state stripped of the necessary information.
      */
-    InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
-        final InsetsState originalState = mStateController.getInsetsForWindowMetrics(attrs);
-        return adjustVisibilityForTransientTypes(originalState);
+    InsetsState enforceInsetsPolicyForTarget(@InternalInsetsType int type,
+            @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop,
+            InsetsState state) {
+        boolean stateCopied = false;
+
+        if (type != ITYPE_INVALID) {
+            state = new InsetsState(state);
+            stateCopied = true;
+            state.removeSource(type);
+
+            // Navigation bar doesn't get influenced by anything else
+            if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
+                state.removeSource(ITYPE_IME);
+                state.removeSource(ITYPE_STATUS_BAR);
+                state.removeSource(ITYPE_CLIMATE_BAR);
+                state.removeSource(ITYPE_CAPTION_BAR);
+                state.removeSource(ITYPE_NAVIGATION_BAR);
+                state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
+            }
+
+            // Status bar doesn't get influenced by caption bar
+            if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
+                state.removeSource(ITYPE_CAPTION_BAR);
+            }
+
+            // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
+            if (type == ITYPE_IME) {
+                ArrayMap<Integer, InsetsSourceProvider> providers = mStateController
+                        .getSourceProviders();
+                for (int i = providers.size() - 1; i >= 0; i--) {
+                    InsetsSourceProvider otherProvider = providers.valueAt(i);
+                    if (otherProvider.overridesImeFrame()) {
+                        InsetsSource override =
+                                new InsetsSource(
+                                        state.getSource(otherProvider.getSource().getType()));
+                        override.setFrame(otherProvider.getImeOverrideFrame());
+                        state.addSource(override);
+                    }
+                }
+            }
+        }
+
+        if (WindowConfiguration.isFloating(windowingMode)
+                || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
+            if (!stateCopied) {
+                state = new InsetsState(state);
+                stateCopied = true;
+            }
+            state.removeSource(ITYPE_STATUS_BAR);
+            state.removeSource(ITYPE_NAVIGATION_BAR);
+            state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
+            if (windowingMode == WINDOWING_MODE_PINNED) {
+                state.removeSource(ITYPE_IME);
+            }
+        }
+
+        return state;
     }
 
     private InsetsState adjustVisibilityForTransientTypes(InsetsState originalState) {
@@ -644,7 +772,7 @@
                 }
                 mAnimatingShown = show;
 
-                final InsetsState state = getInsetsForWindow(mFocusedWin);
+                final InsetsState state = mFocusedWin.getInsetsState();
 
                 // We are about to playing the default animation. Passing a null frame indicates
                 // the controlled types should be animated regardless of the frame.
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 21eea94..4c7a297 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -69,7 +69,7 @@
 
     protected final DisplayContent mDisplayContent;
     protected final @NonNull InsetsSource mSource;
-    protected WindowState mWin;
+    protected WindowContainer mWindowContainer;
 
     private final Rect mTmpRect = new Rect();
     private final InsetsStateController mStateController;
@@ -80,8 +80,8 @@
     private @Nullable InsetsControlTarget mFakeControlTarget;
 
     private @Nullable ControlAdapter mAdapter;
-    private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
-    private TriConsumer<DisplayFrames, WindowState, Rect> mImeFrameProvider;
+    private TriConsumer<DisplayFrames, WindowContainer, Rect> mFrameProvider;
+    private TriConsumer<DisplayFrames, WindowContainer, Rect> mImeFrameProvider;
     private final Rect mImeOverrideFrame = new Rect();
     private boolean mIsLeashReadyForDispatching;
     private final Rect mLastSourceFrame = new Rect();
@@ -100,7 +100,8 @@
     private boolean mClientVisible;
 
     /**
-     * Whether the window is available and considered visible as in {@link WindowState#isVisible}.
+     * Whether the window container is available and considered visible as in
+     * {@link WindowContainer#isVisible}.
      */
     private boolean mServerVisible;
 
@@ -109,8 +110,8 @@
     private final boolean mControllable;
 
     /**
-     * Whether to forced the dimensions of the source window to the inset frame and crop out any
-     * overflow.
+     * Whether to forced the dimensions of the source window container to the inset frame and crop
+     * out any overflow.
      * Used to crop the taskbar inset source when a task animation is occurring to hide the taskbar
      * rounded corners overlays.
      *
@@ -152,42 +153,42 @@
     }
 
     /**
-     * Updates the window that currently backs this source.
+     * Updates the window container that currently backs this source.
      *
-     * @param win The window that links to this source.
+     * @param windowContainer The window container that links to this source.
      * @param frameProvider Based on display frame state and the window, calculates the resulting
      *                      frame that should be reported to clients.
      * @param imeFrameProvider Based on display frame state and the window, calculates the resulting
      *                         frame that should be reported to IME.
      */
-    void setWindow(@Nullable WindowState win,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider) {
-        if (mWin != null) {
+    void setWindowContainer(@Nullable WindowContainer windowContainer,
+            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider,
+            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider) {
+        if (mWindowContainer != null) {
             if (mControllable) {
-                mWin.setControllableInsetProvider(null);
+                mWindowContainer.setControllableInsetProvider(null);
             }
-            // The window may be animating such that we can hand out the leash to the control
-            // target. Revoke the leash by cancelling the animation to correct the state.
+            // The window container may be animating such that we can hand out the leash to the
+            // control target. Revoke the leash by cancelling the animation to correct the state.
             // TODO: Ideally, we should wait for the animation to finish so previous window can
             // animate-out as new one animates-in.
-            mWin.cancelAnimation();
-            mWin.mProvidedInsetsSources.remove(mSource.getType());
+            mWindowContainer.cancelAnimation();
+            mWindowContainer.getProvidedInsetsSources().remove(mSource.getType());
             mSeamlessRotating = false;
         }
-        ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource setWin %s for type %s", win,
-                InsetsState.typeToString(mSource.getType()));
-        mWin = win;
+        ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource setWin %s for type %s",
+                windowContainer, InsetsState.typeToString(mSource.getType()));
+        mWindowContainer = windowContainer;
         mFrameProvider = frameProvider;
         mImeFrameProvider = imeFrameProvider;
-        if (win == null) {
+        if (windowContainer == null) {
             setServerVisible(false);
             mSource.setFrame(new Rect());
             mSource.setVisibleFrame(null);
         } else {
-            mWin.mProvidedInsetsSources.put(mSource.getType(), mSource);
+            mWindowContainer.getProvidedInsetsSources().put(mSource.getType(), mSource);
             if (mControllable) {
-                mWin.setControllableInsetProvider(this);
+                mWindowContainer.setControllableInsetProvider(this);
                 if (mPendingControlTarget != null) {
                     updateControlForTarget(mPendingControlTarget, true /* force */);
                     mPendingControlTarget = null;
@@ -197,18 +198,39 @@
     }
 
     /**
-     * @return Whether there is a window which backs this source.
+     * @return Whether there is a window container which backs this source.
      */
-    boolean hasWindow() {
-        return mWin != null;
+    boolean hasWindowContainer() {
+        return mWindowContainer != null;
     }
 
     /**
      * The source frame can affect the layout of other windows, so this should be called once the
-     * window gets laid out.
+     * window container gets laid out.
      */
     void updateSourceFrame() {
-        if (mWin == null || mWin.mGivenInsetsPending) {
+        if (mWindowContainer == null) {
+            return;
+        }
+        WindowState win = mWindowContainer.asWindowState();
+
+        if (win == null) {
+            // For all the non window WindowContainers.
+            if (mServerVisible) {
+                mTmpRect.set(mWindowContainer.getBounds());
+                if (mFrameProvider != null) {
+                    mFrameProvider.accept(mWindowContainer.getDisplayContent().mDisplayFrames,
+                            mWindowContainer, mTmpRect);
+                }
+            } else {
+                mTmpRect.setEmpty();
+            }
+            mSource.setFrame(mTmpRect);
+            mSource.setVisibleFrame(null);
+            return;
+        }
+
+        if (win.mGivenInsetsPending) {
             // If the given insets are pending, they are not reliable for now. The source frame
             // should be updated after the new given insets are sent to window manager.
             return;
@@ -218,11 +240,12 @@
         // frame may not yet determined that server side doesn't think the window is ready to
         // visible. (i.e. No surface, pending insets that were given during layout, etc..)
         if (mServerVisible) {
-            mTmpRect.set(mWin.getFrame());
+            mTmpRect.set(win.getFrame());
             if (mFrameProvider != null) {
-                mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
+                mFrameProvider.accept(mWindowContainer.getDisplayContent().mDisplayFrames,
+                        mWindowContainer, mTmpRect);
             } else {
-                mTmpRect.inset(mWin.mGivenContentInsets);
+                mTmpRect.inset(win.mGivenContentInsets);
             }
         } else {
             mTmpRect.setEmpty();
@@ -230,15 +253,17 @@
         mSource.setFrame(mTmpRect);
 
         if (mImeFrameProvider != null) {
-            mImeOverrideFrame.set(mWin.getFrame());
-            mImeFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin,
+            mImeOverrideFrame.set(win.getFrame());
+            mImeFrameProvider.accept(mWindowContainer.getDisplayContent().mDisplayFrames,
+                    mWindowContainer,
                     mImeOverrideFrame);
         }
 
-        if (mWin.mGivenVisibleInsets.left != 0 || mWin.mGivenVisibleInsets.top != 0
-                || mWin.mGivenVisibleInsets.right != 0 || mWin.mGivenVisibleInsets.bottom != 0) {
-            mTmpRect.set(mWin.getFrame());
-            mTmpRect.inset(mWin.mGivenVisibleInsets);
+        if (win.mGivenVisibleInsets.left != 0 || win.mGivenVisibleInsets.top != 0
+                || win.mGivenVisibleInsets.right != 0
+                || win.mGivenVisibleInsets.bottom != 0) {
+            mTmpRect.set(win.getFrame());
+            mTmpRect.inset(win.mGivenVisibleInsets);
             mSource.setVisibleFrame(mTmpRect);
         } else {
             mSource.setVisibleFrame(null);
@@ -253,7 +278,7 @@
         source.setVisible(mSource.isVisible());
         mTmpRect.set(winFrame);
         if (mFrameProvider != null) {
-            mFrameProvider.accept(displayFrames, mWin, mTmpRect);
+            mFrameProvider.accept(displayFrames, mWindowContainer, mTmpRect);
         }
         source.setFrame(mTmpRect);
         return source;
@@ -263,27 +288,30 @@
      * Called when a layout pass has occurred.
      */
     void onPostLayout() {
-        if (mWin == null) {
+        if (mWindowContainer == null) {
             return;
         }
-
-        setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy());
+        WindowState windowState = mWindowContainer.asWindowState();
+        boolean isServerVisible = windowState != null
+                ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
+                : mWindowContainer.isVisibleRequested();
+        setServerVisible(isServerVisible);
         updateSourceFrame();
         if (mControl != null) {
             boolean changed = false;
             final Point position = getWindowFrameSurfacePosition();
             if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) {
                 changed = true;
-                if (mWin.getWindowFrames().didFrameSizeChange() && mWin.mWinAnimator.getShown()
-                        && mWin.okToDisplay()) {
-                    mWin.applyWithNextDraw(mSetLeashPositionConsumer);
+                if (windowState != null && windowState.getWindowFrames().didFrameSizeChange()
+                        && windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) {
+                    windowState.applyWithNextDraw(mSetLeashPositionConsumer);
                 } else {
-                    mSetLeashPositionConsumer.accept(mWin.getSyncTransaction());
+                    mSetLeashPositionConsumer.accept(mWindowContainer.getSyncTransaction());
                 }
             }
             if (mServerVisible && !mLastSourceFrame.equals(mSource.getFrame())) {
                 final Insets insetsHint = mSource.calculateInsets(
-                        mWin.getBounds(), true /* ignoreVisibility */);
+                        mWindowContainer.getBounds(), true /* ignoreVisibility */);
                 if (!insetsHint.equals(mControl.getInsetsHint())) {
                     changed = true;
                     mControl.setInsetsHint(insetsHint);
@@ -297,17 +325,19 @@
     }
 
     private Point getWindowFrameSurfacePosition() {
+        WindowState win = mWindowContainer.asWindowState();
         if (mControl != null) {
             final AsyncRotationController controller =
-                    mWin.mDisplayContent.getAsyncRotationController();
-            if (controller != null && controller.shouldFreezeInsetsPosition(mWin)) {
+                    win.mDisplayContent.getAsyncRotationController();
+            if (controller != null && controller.shouldFreezeInsetsPosition(win)) {
                 // Use previous position because the fade-out animation runs in old rotation.
                 return mControl.getSurfacePosition();
             }
         }
-        final Rect frame = mWin.getFrame();
+        final Rect frame = mWindowContainer.asWindowState() != null
+                ? mWindowContainer.asWindowState().getFrame() : mWindowContainer.getBounds();
         final Point position = new Point();
-        mWin.transformFrameToSurfacePosition(frame.left, frame.top, position);
+        mWindowContainer.transformFrameToSurfacePosition(frame.left, frame.top, position);
         return position;
     }
 
@@ -322,8 +352,8 @@
     }
 
     /**
-     * Ensures that the inset source window is cropped so that anything that doesn't fit within the
-     * inset frame is cropped out until removeCropToProvidingInsetsBounds is called.
+     * Ensures that the inset source window container is cropped so that anything that doesn't fit
+     * within the inset frame is cropped out until removeCropToProvidingInsetsBounds is called.
      *
      * The inset source surface will get cropped to the be of the size of the insets it's providing.
      *
@@ -342,9 +372,10 @@
     void setCropToProvidingInsetsBounds(Transaction t) {
         mCropToProvidingInsets = true;
 
-        if (mWin != null && mWin.mSurfaceAnimator.hasLeash()) {
+        if (mWindowContainer != null && mWindowContainer.mSurfaceAnimator.hasLeash()) {
             // apply to existing leash
-            t.setWindowCrop(mWin.mSurfaceAnimator.mLeash, getProvidingInsetsBoundsCropRect());
+            t.setWindowCrop(mWindowContainer.mSurfaceAnimator.mLeash,
+                    getProvidingInsetsBoundsCropRect());
         }
     }
 
@@ -359,13 +390,15 @@
         mCropToProvidingInsets = false;
 
         // apply to existing leash
-        if (mWin != null && mWin.mSurfaceAnimator.hasLeash()) {
-            t.setWindowCrop(mWin.mSurfaceAnimator.mLeash, null);
+        if (mWindowContainer != null && mWindowContainer.mSurfaceAnimator.hasLeash()) {
+            t.setWindowCrop(mWindowContainer.mSurfaceAnimator.mLeash, null);
         }
     }
 
     private Rect getProvidingInsetsBoundsCropRect() {
-        Rect sourceWindowFrame = mWin.getFrame();
+        Rect sourceWindowFrame = mWindowContainer.asWindowState() != null
+                ? mWindowContainer.asWindowState().getFrame()
+                : mWindowContainer.getBounds();
         Rect insetFrame = getSource().getFrame();
 
         // The rectangle in buffer space we want to crop to
@@ -384,11 +417,11 @@
             return;
         }
 
-        if (mWin != null && mWin.getSurfaceControl() == null) {
+        if (mWindowContainer != null && mWindowContainer.getSurfaceControl() == null) {
             // if window doesn't have a surface, set it null and return.
-            setWindow(null, null, null);
+            setWindowContainer(null, null, null);
         }
-        if (mWin == null) {
+        if (mWindowContainer == null) {
             mPendingControlTarget = target;
             return;
         }
@@ -397,7 +430,7 @@
         }
         if (target == null) {
             // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
-            mWin.cancelAnimation();
+            mWindowContainer.cancelAnimation();
             setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
             return;
         }
@@ -407,7 +440,7 @@
             setClientVisible(target.getRequestedVisibility(mSource.getType()));
         }
         final Transaction t = mDisplayContent.getSyncTransaction();
-        mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
+        mWindowContainer.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
                 ANIMATION_TYPE_INSETS_CONTROL);
 
         // The leash was just created. We cannot dispatch it until its surface transaction is
@@ -418,16 +451,16 @@
         mControlTarget = target;
         updateVisibility();
         mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition,
-                mSource.calculateInsets(mWin.getBounds(), true /* ignoreVisibility */));
+                mSource.calculateInsets(mWindowContainer.getBounds(), true /* ignoreVisibility */));
         ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
                 "InsetsSource Control %s for target %s", mControl, mControlTarget);
     }
 
     void startSeamlessRotation() {
-      if (!mSeamlessRotating) {
-          mSeamlessRotating = true;
-          mWin.cancelAnimation();
-      }
+        if (!mSeamlessRotating) {
+            mSeamlessRotating = true;
+            mWindowContainer.cancelAnimation();
+        }
     }
 
     void finishSeamlessRotation() {
@@ -475,10 +508,13 @@
     }
 
     private boolean isMirroredSource() {
-        if (mWin == null) {
+        if (mWindowContainer == null) {
             return false;
         }
-        final int[] provides = mWin.mAttrs.providesInsetsTypes;
+        if (mWindowContainer.asWindowState() == null) {
+            return false;
+        }
+        final int[] provides = ((WindowState) mWindowContainer).mAttrs.providesInsetsTypes;
         if (provides == null) {
             return false;
         }
@@ -542,9 +578,9 @@
         pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
         pw.print(" mImeOverrideFrame="); pw.print(mImeOverrideFrame.toShortString());
         pw.println();
-        if (mWin != null) {
-            pw.print(prefix + "mWin=");
-            pw.println(mWin);
+        if (mWindowContainer != null) {
+            pw.print(prefix + "mWindowContainer=");
+            pw.println(mWindowContainer);
         }
         if (mAdapter != null) {
             pw.print(prefix + "mAdapter=");
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index a1468cc..32e70d9 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,30 +16,20 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
-import static android.view.InsetsState.ITYPE_INVALID;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.WindowInsets.Type.displayCutout;
 import static android.view.WindowInsets.Type.mandatorySystemGestures;
 import static android.view.WindowInsets.Type.systemGestures;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.WindowConfiguration;
-import android.app.WindowConfiguration.WindowingMode;
 import android.graphics.Rect;
 import android.os.Trace;
 import android.util.ArrayMap;
@@ -49,8 +39,6 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams.WindowType;
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -103,134 +91,6 @@
         mDisplayContent = displayContent;
     }
 
-    /**
-     * Gets the insets state from the perspective of the target. When performing layout of the
-     * target or dispatching insets to the target, we need to exclude sources which should not be
-     * visible to the target. e.g., the source which represents the target window itself, and the
-     * IME source when the target is above IME. We also need to exclude certain types of insets
-     * source for client within specific windowing modes.
-     * This is to get the insets for a window layout on the screen. If the window is not there, use
-     * the {@link #getInsetsForWindowMetrics} to get insets instead.
-     *
-     * @param target The window associate with the perspective.
-     * @return The state stripped of the necessary information.
-     */
-    InsetsState getInsetsForWindow(@NonNull WindowState target) {
-        final InsetsState rotatedState = target.mToken.getFixedRotationTransformInsetsState();
-        if (rotatedState != null) {
-            return rotatedState;
-        }
-        final InsetsSourceProvider provider = target.getControllableInsetProvider();
-        final @InternalInsetsType int type = provider != null
-                ? provider.getSource().getType() : ITYPE_INVALID;
-        return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
-                target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() :
-                        (target.mAttrs.receiveInsetsIgnoringZOrder ? mState :
-                         target.mAboveInsetsState));
-    }
-
-    InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
-        final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs);
-        final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
-        if (token != null) {
-            final InsetsState rotatedState = token.getFixedRotationTransformInsetsState();
-            if (rotatedState != null) {
-                return rotatedState;
-            }
-        }
-        final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
-        // Always use windowing mode fullscreen when get insets for window metrics to make sure it
-        // contains all insets types.
-        return getInsetsForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop, mState);
-    }
-
-    private static @InternalInsetsType
-            int getInsetsTypeForLayoutParams(WindowManager.LayoutParams attrs) {
-        @WindowType int type = attrs.type;
-        switch (type) {
-            case TYPE_STATUS_BAR:
-                return ITYPE_STATUS_BAR;
-            case TYPE_NAVIGATION_BAR:
-                return ITYPE_NAVIGATION_BAR;
-            case TYPE_INPUT_METHOD:
-                return ITYPE_IME;
-        }
-
-        // If not one of the types above, check whether an internal inset mapping is specified.
-        if (attrs.providesInsetsTypes != null) {
-            for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
-                switch (insetsType) {
-                    case ITYPE_STATUS_BAR:
-                    case ITYPE_NAVIGATION_BAR:
-                    case ITYPE_CLIMATE_BAR:
-                    case ITYPE_EXTRA_NAVIGATION_BAR:
-                        return insetsType;
-                }
-            }
-        }
-
-        return ITYPE_INVALID;
-    }
-
-    /**
-     * @see #getInsetsForWindow
-     * @see #getInsetsForWindowMetrics
-     */
-    private InsetsState getInsetsForTarget(@InternalInsetsType int type,
-            @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state) {
-        boolean stateCopied = false;
-
-        if (type != ITYPE_INVALID) {
-            state = new InsetsState(state);
-            stateCopied = true;
-            state.removeSource(type);
-
-            // Navigation bar doesn't get influenced by anything else
-            if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
-                state.removeSource(ITYPE_STATUS_BAR);
-                state.removeSource(ITYPE_CLIMATE_BAR);
-                state.removeSource(ITYPE_CAPTION_BAR);
-                state.removeSource(ITYPE_NAVIGATION_BAR);
-                state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
-            }
-
-            // Status bar doesn't get influenced by caption bar
-            if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
-                state.removeSource(ITYPE_CAPTION_BAR);
-            }
-
-            // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
-            if (type == ITYPE_IME) {
-                for (int i = mProviders.size() - 1; i >= 0; i--) {
-                    InsetsSourceProvider otherProvider = mProviders.valueAt(i);
-                    if (otherProvider.overridesImeFrame()) {
-                        InsetsSource override =
-                                new InsetsSource(
-                                        state.getSource(otherProvider.getSource().getType()));
-                        override.setFrame(otherProvider.getImeOverrideFrame());
-                        state.addSource(override);
-                    }
-                }
-            }
-        }
-
-        if (WindowConfiguration.isFloating(windowingMode)
-                || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
-            if (!stateCopied) {
-                state = new InsetsState(state);
-                stateCopied = true;
-            }
-            state.removeSource(ITYPE_STATUS_BAR);
-            state.removeSource(ITYPE_NAVIGATION_BAR);
-            state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
-            if (windowingMode == WINDOWING_MODE_PINNED) {
-                state.removeSource(ITYPE_IME);
-            }
-        }
-
-        return state;
-    }
-
     InsetsState getRawInsetsState() {
         return mState;
     }
@@ -248,6 +108,10 @@
         return result;
     }
 
+    ArrayMap<Integer, InsetsSourceProvider> getSourceProviders() {
+        return mProviders;
+    }
+
     /**
      * @return The provider of a specific type.
      */
@@ -303,7 +167,7 @@
         final InsetsState aboveInsetsState = new InsetsState();
         aboveInsetsState.set(mState,
                 displayCutout() | systemGestures() | mandatorySystemGestures());
-        final SparseArray<InsetsSource> winProvidedSources = win.mProvidedInsetsSources;
+        final SparseArray<InsetsSource> winProvidedSources = win.getProvidedInsetsSources();
         final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>();
         mDisplayContent.forAllWindows(w -> {
             if (aboveWin[0]) {
@@ -315,7 +179,7 @@
                     }
                     return winProvidedSources.size() == 0;
                 } else {
-                    final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources;
+                    final SparseArray<InsetsSource> providedSources = w.getProvidedInsetsSources();
                     for (int i = providedSources.size() - 1; i >= 0; i--) {
                         aboveInsetsState.addSource(providedSources.valueAt(i));
                     }
@@ -382,7 +246,7 @@
         final InsetsState state = displayFrames.mInsetsState;
         for (int i = mProviders.size() - 1; i >= 0; i--) {
             final InsetsSourceProvider provider = mProviders.valueAt(i);
-            if (provider.mWin == win) {
+            if (provider.mWindowContainer == win) {
                 state.addSource(provider.createSimulatedSource(displayFrames, winFrame));
             }
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d535018..8ab2ee0 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1979,7 +1979,8 @@
                 onTop);
     }
 
-    void moveActivityToPinnedRootTask(ActivityRecord r, String reason) {
+    void moveActivityToPinnedRootTask(@NonNull ActivityRecord r,
+            @Nullable ActivityRecord launchIntoPipHostActivity, String reason) {
         mService.deferWindowLayout();
 
         final TaskDisplayArea taskDisplayArea = r.getDisplayArea();
@@ -2030,7 +2031,7 @@
                         .setHasBeenVisible(true)
                         .build();
                 // Establish bi-directional link between the original and pinned task.
-                r.setLastParentBeforePip();
+                r.setLastParentBeforePip(launchIntoPipHostActivity);
                 // It's possible the task entering PIP is in freeform, so save the last
                 // non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore
                 // to its previous freeform bounds.
@@ -2091,6 +2092,10 @@
             r.setWindowingMode(intermediateWindowingMode);
             r.mWaitForEnteringPinnedMode = true;
             rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
+            // Set the launch bounds for launch-into-pip Activity on the root task.
+            if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {
+                rootTask.setBounds(r.getOptions().getLaunchBounds());
+            }
             rootTask.setDeferTaskAppear(false);
 
             // Reset the state that indicates it can enter PiP while pausing after we've moved it
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index d1460f4..6bb63d9 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -108,15 +108,12 @@
      */
     private SurfaceControl mBackColorSurface;
     private BlackFrame mEnteringBlackFrame;
-    private int mWidth, mHeight;
 
     private final int mOriginalRotation;
     private final int mOriginalWidth;
     private final int mOriginalHeight;
     private int mCurRotation;
 
-    private Rect mOriginalDisplayRect = new Rect();
-    private Rect mCurrentDisplayRect = new Rect();
     // The current active animation to move from the old to the new rotated
     // state.  Which animation is run here will depend on the old and new
     // rotations.
@@ -127,7 +124,6 @@
     private boolean mAnimRunning;
     private boolean mFinishAnimReady;
     private long mFinishAnimStartTime;
-    private boolean mForceDefaultOrientation;
     private SurfaceRotationAnimationController mSurfaceRotationAnimationController;
     /** Intensity of light/whiteness of the layout before rotation occurs. */
     private float mStartLuma;
@@ -138,25 +134,13 @@
         mService = displayContent.mWmService;
         mContext = mService.mContext;
         mDisplayContent = displayContent;
-        displayContent.getBounds(mOriginalDisplayRect);
+        final Rect currentBounds = displayContent.getBounds();
+        final int width = currentBounds.width();
+        final int height = currentBounds.height();
 
         // Screenshot does NOT include rotation!
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         final int realOriginalRotation = displayInfo.rotation;
-        final int originalWidth;
-        final int originalHeight;
-        if (displayContent.getDisplayRotation().isFixedToUserRotation()) {
-            // Emulated orientation.
-            mForceDefaultOrientation = true;
-            originalWidth = displayContent.mBaseDisplayWidth;
-            originalHeight = displayContent.mBaseDisplayHeight;
-        } else {
-            // Normal situation
-            originalWidth = displayInfo.logicalWidth;
-            originalHeight = displayInfo.logicalHeight;
-        }
-        mWidth = originalWidth;
-        mHeight = originalHeight;
 
         mOriginalRotation = originalRotation;
         // If the delta is not zero, the rotation of display may not change, but we still want to
@@ -165,8 +149,8 @@
         // when restoring the rotated app to the same rotation as current display.
         final int delta = deltaRotation(originalRotation, realOriginalRotation);
         final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270;
-        mOriginalWidth = flipped ? originalHeight : originalWidth;
-        mOriginalHeight = flipped ? originalWidth : originalHeight;
+        mOriginalWidth = flipped ? height : width;
+        mOriginalHeight = flipped ? width : height;
         mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();
 
         // Check whether the current screen contains any secure content.
@@ -179,7 +163,7 @@
                     new SurfaceControl.LayerCaptureArgs.Builder(displayContent.getSurfaceControl())
                             .setCaptureSecureLayers(true)
                             .setAllowProtected(true)
-                            .setSourceCrop(new Rect(0, 0, mWidth, mHeight))
+                            .setSourceCrop(new Rect(0, 0, width, height))
                             .build();
             SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                     SurfaceControl.captureLayers(args);
@@ -244,6 +228,19 @@
             Slog.w(TAG, "Unable to allocate freeze surface", e);
         }
 
+        // If display size is changed with the same orientation, map the bounds of screenshot to
+        // the new logical display size. Currently pending transaction and RWC#mDisplayTransaction
+        // are merged to global transaction, so it can be synced with display change when calling
+        // DisplayManagerInternal#performTraversal(transaction).
+        final int logicalWidth = displayInfo.logicalWidth;
+        final int logicalHeight = displayInfo.logicalHeight;
+        if (logicalWidth > mOriginalWidth == logicalHeight > mOriginalHeight
+                && (logicalWidth != mOriginalWidth || logicalHeight != mOriginalHeight)) {
+            displayContent.getPendingTransaction().setGeometry(mScreenshotLayer,
+                    new Rect(0, 0, mOriginalWidth, mOriginalHeight),
+                    new Rect(0, 0, logicalWidth, logicalHeight), Surface.ROTATION_0);
+        }
+
         ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
                 "  FREEZE %s: CREATE", mScreenshotLayer);
         if (originalRotation == realOriginalRotation) {
@@ -277,11 +274,6 @@
         matrix.getValues(mTmpFloats);
         float x = mTmpFloats[Matrix.MTRANS_X];
         float y = mTmpFloats[Matrix.MTRANS_Y];
-        if (mForceDefaultOrientation) {
-            mDisplayContent.getBounds(mCurrentDisplayRect);
-            x -= mCurrentDisplayRect.left;
-            y -= mCurrentDisplayRect.top;
-        }
         t.setPosition(mScreenshotLayer, x, y);
         t.setMatrix(mScreenshotLayer,
                 mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
@@ -293,8 +285,6 @@
 
     public void printTo(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("mSurface="); pw.print(mScreenshotLayer);
-        pw.print(" mWidth="); pw.print(mWidth);
-        pw.print(" mHeight="); pw.println(mHeight);
         pw.print(prefix);
         pw.print("mEnteringBlackFrame=");
         pw.println(mEnteringBlackFrame);
@@ -315,11 +305,6 @@
         pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
         pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
         mSnapshotInitialMatrix.dump(pw); pw.println();
-        pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
-        if (mForceDefaultOrientation) {
-            pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString());
-            pw.print(" mCurrentDisplayRect="); pw.println(mCurrentDisplayRect.toShortString());
-        }
     }
 
     public void setRotation(SurfaceControl.Transaction t, int rotation) {
@@ -329,7 +314,8 @@
         // to the snapshot to make it stay in the same original position
         // with the current screen rotation.
         int delta = deltaRotation(rotation, mOriginalRotation);
-        RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
+        RotationAnimationUtils.createRotationMatrix(delta, mOriginalWidth, mOriginalHeight,
+                mSnapshotInitialMatrix);
         setRotationTransform(t, mSnapshotInitialMatrix);
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 58cf4bb..358a615 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3398,6 +3398,12 @@
 
         info.pictureInPictureParams = getPictureInPictureParams(top);
         info.preferDockBigOverlays = getPreferDockBigOverlays();
+        if (info.pictureInPictureParams != null
+                && info.pictureInPictureParams.isLaunchIntoPip()
+                && top.getTopMostActivity().getLastParentBeforePip() != null) {
+            info.launchIntoPipHostTaskId =
+                    top.getTopMostActivity().getLastParentBeforePip().mTaskId;
+        }
         info.displayCutoutInsets = top != null ? top.getDisplayCutoutInsets() : null;
         info.topActivityInfo = mReuseActivitiesReport.top != null
                 ? mReuseActivitiesReport.top.info
@@ -6576,8 +6582,7 @@
             return;
         }
         if (mOverlayHost != null) {
-            final InsetsState s = getDisplayContent().getInsetsPolicy()
-                .getInsetsForWindow(originalChange, true);
+            final InsetsState s = originalChange.getInsetsState(true);
             getBounds(mTmpRect);
             mOverlayHost.dispatchInsetsChanged(s, mTmpRect);
         }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 1bba103..f0cca18 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -111,9 +110,6 @@
     private Task mRootHomeTask;
     private Task mRootPinnedTask;
 
-    // TODO(b/159029784): Remove when getStack() behavior is cleaned-up
-    private Task mRootRecentsTask;
-
     private final ArrayList<WindowContainer> mTmpAlwaysOnTopChildren = new ArrayList<>();
     private final ArrayList<WindowContainer> mTmpNormalChildren = new ArrayList<>();
     private final ArrayList<WindowContainer> mTmpHomeChildren = new ArrayList<>();
@@ -222,8 +218,6 @@
     Task getRootTask(int windowingMode, int activityType) {
         if (activityType == ACTIVITY_TYPE_HOME) {
             return mRootHomeTask;
-        } else if (activityType == ACTIVITY_TYPE_RECENTS) {
-            return mRootRecentsTask;
         }
         if (windowingMode == WINDOWING_MODE_PINNED) {
             return mRootPinnedTask;
@@ -249,11 +243,6 @@
         return mRootHomeTask;
     }
 
-    @Nullable
-    Task getRootRecentsTask() {
-        return mRootRecentsTask;
-    }
-
     Task getRootPinnedTask() {
         return mRootPinnedTask;
     }
@@ -288,16 +277,6 @@
             } else {
                 mRootHomeTask = rootTask;
             }
-        } else if (rootTask.isActivityTypeRecents()) {
-            if (mRootRecentsTask != null) {
-                if (!rootTask.isDescendantOf(mRootRecentsTask)) {
-                    throw new IllegalArgumentException("addRootTaskReferenceIfNeeded: root recents"
-                            + " task=" + mRootRecentsTask + " already exist on display=" + this
-                            + " rootTask=" + rootTask);
-                }
-            } else {
-                mRootRecentsTask = rootTask;
-            }
         }
 
         if (!rootTask.isRootTask()) {
@@ -317,8 +296,6 @@
     void removeRootTaskReferenceIfNeeded(Task rootTask) {
         if (rootTask == mRootHomeTask) {
             mRootHomeTask = null;
-        } else if (rootTask == mRootRecentsTask) {
-            mRootRecentsTask = null;
         } else if (rootTask == mRootPinnedTask) {
             mRootPinnedTask = null;
         }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index dd1f29e..7fab94c 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -31,6 +31,7 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
 import static android.os.UserHandle.USER_NULL;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -82,6 +83,7 @@
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -530,6 +532,11 @@
      * certificate.</li>
      */
     private boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a) {
+        if (UserHandle.getAppId(mTaskFragmentOrganizerUid) == SYSTEM_UID) {
+            // The system is trusted to embed other apps securely and for all users.
+            return true;
+        }
+
         if (mTaskFragmentOrganizerUid == a.getUid()) {
             // Activities from the same UID can be embedded freely by the host.
             return true;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index cbef60c..0b965c3 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1251,6 +1251,17 @@
                 final ActivityRecord topMostActivity = task.getTopMostActivity();
                 change.setAllowEnterPip(topMostActivity != null
                         && topMostActivity.checkEnterPictureInPictureAppOpsState());
+                final ActivityRecord topRunningActivity = task.topRunningActivity();
+                if (topRunningActivity != null && task.mDisplayContent != null) {
+                    // If Activity is in fixed rotation, its will be applied with the next rotation,
+                    // when the Task is still in the previous rotation.
+                    final int taskRotation = task.getWindowConfiguration().getDisplayRotation();
+                    final int activityRotation = topRunningActivity.getWindowConfiguration()
+                            .getDisplayRotation();
+                    if (taskRotation != activityRotation) {
+                        change.setEndFixedRotation(activityRotation);
+                    }
+                }
             } else if ((info.mFlags & ChangeInfo.FLAG_SEAMLESS_ROTATION) != 0) {
                 change.setRotationAnimation(ROTATION_ANIMATION_SEAMLESS);
             }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e1746cc..45fdc04 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -81,8 +81,10 @@
 import android.util.Pools;
 import android.util.RotationUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
+import android.view.InsetsSource;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
@@ -127,7 +129,8 @@
  * changes are made to this class.
  */
 class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
-        implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable {
+        implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
+        InsetsControlTarget {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
 
@@ -145,6 +148,13 @@
     // onParentChanged() notification.
     boolean mReparenting;
 
+    protected @Nullable InsetsSourceProvider mControllableInsetProvider;
+
+    /**
+     * The insets sources provided by this windowContainer.
+     */
+    private SparseArray<InsetsSource> mProvidedInsetsSources = null;
+
     // List of children for this window container. List is in z-order as the children appear on
     // screen with the top-most window container at the tail of the list.
     protected final WindowList<E> mChildren = new WindowList<E>();
@@ -329,6 +339,25 @@
         mSurfaceFreezer = new SurfaceFreezer(this, wms);
     }
 
+    /**
+     * Set's an {@link InsetsSourceProvider} to be associated with this window, but only if the
+     * provider itself is controllable, as one window can be the provider of more than one inset
+     * type (i.e. gesture insets). If this window is controllable, all its animations must be
+     * controlled by its control target, and the visibility of this window should be taken account
+     * into the state of the control target.
+     *
+     * @param insetProvider the provider which should not be visible to the client.
+     * @see #getInsetsState()
+     */
+    void setControllableInsetProvider(InsetsSourceProvider insetProvider) {
+        mControllableInsetProvider = insetProvider;
+    }
+
+    InsetsSourceProvider getControllableInsetProvider() {
+        return mControllableInsetProvider;
+    }
+
+
     @Override
     final protected WindowContainer getParent() {
         return mParent;
@@ -858,6 +887,13 @@
         }
     }
 
+    public SparseArray<InsetsSource> getProvidedInsetsSources() {
+        if (mProvidedInsetsSources == null) {
+            mProvidedInsetsSources = new SparseArray<>();
+        }
+        return mProvidedInsetsSources;
+    }
+
     DisplayContent getDisplayContent() {
         return mDisplayContent;
     }
@@ -2962,6 +2998,16 @@
         scheduleAnimation();
     }
 
+    void transformFrameToSurfacePosition(int left, int top, Point outPoint) {
+        outPoint.set(left, top);
+        final WindowContainer parentWindowContainer = getParent();
+        if (parentWindowContainer == null) {
+            return;
+        }
+        final Rect parentBounds = parentWindowContainer.getBounds();
+        outPoint.offset(-parentBounds.left, -parentBounds.top);
+    }
+
     void reassignLayer(Transaction t) {
         final WindowContainer parent = getParent();
         if (parent != null) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 49f742c..1ab8cbf 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -30,6 +30,7 @@
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.ITYPE_INVALID;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.SurfaceControl.Transaction;
 import static android.view.SurfaceControl.getGlobalTransaction;
@@ -216,7 +217,6 @@
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
@@ -680,11 +680,6 @@
     final InsetsState mAboveInsetsState = new InsetsState();
 
     /**
-     * The insets sources provided by this window.
-     */
-    final SparseArray<InsetsSource> mProvidedInsetsSources = new SparseArray<>();
-
-    /**
      * Surface insets from the previous call to relayout(), used to track
      * if we are changing the Surface insets.
      */
@@ -738,7 +733,6 @@
      */
     private boolean mIsDimming = false;
 
-    private @Nullable InsetsSourceProvider mControllableInsetProvider;
     private final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
 
     /**
@@ -1647,11 +1641,39 @@
     }
 
     /**
-     * Returns the insets state for the window. Its sources may be the copies with visibility
-     * modification according to the state of transient bars.
+     * See {@link WindowState#getInsetsState(boolean)}
      */
     InsetsState getInsetsState() {
-        return getDisplayContent().getInsetsPolicy().getInsetsForWindow(this);
+        return getInsetsState(false);
+    }
+
+    /**
+     * Returns the insets state for the window. Its sources may be the copies with visibility
+     * modification according to the state of transient bars.
+     * This is to get the insets for a window layout on the screen. If the window is not there, use
+     * the {@link InsetsPolicy#getInsetsForWindowMetrics} to get insets instead.
+     * @param includeTransient whether or not the transient types should be included in the
+     *                         insets state.
+     */
+    InsetsState getInsetsState(boolean includeTransient) {
+        final InsetsState rotatedState = mToken.getFixedRotationTransformInsetsState();
+        final InsetsPolicy insetsPolicy = getDisplayContent().getInsetsPolicy();
+        if (rotatedState != null) {
+            return insetsPolicy.adjustInsetsForWindow(this, rotatedState);
+        }
+        final InsetsSourceProvider provider = getControllableInsetProvider();
+        final InsetsStateController insetsStateController = getDisplayContent()
+                .getInsetsStateController();
+        final @InternalInsetsType int insetTypeProvidedByWindow = provider != null
+                ? provider.getSource().getType() : ITYPE_INVALID;
+        final InsetsState rawInsetsState = getFrozenInsetsState() != null
+                ? getFrozenInsetsState() : (mAttrs.receiveInsetsIgnoringZOrder
+                ? insetsStateController.getRawInsetsState() : mAboveInsetsState);
+        final InsetsState insetsStateForWindow = insetsPolicy
+                .enforceInsetsPolicyForTarget(insetTypeProvidedByWindow,
+                        getWindowingMode(), isAlwaysOnTop(), rawInsetsState);
+        return insetsPolicy.adjustInsetsForWindow(this, insetsStateForWindow,
+                includeTransient);
     }
 
     /**
@@ -5658,24 +5680,6 @@
         mWindowFrames.setContentChanged(false);
     }
 
-    /**
-     * Set's an {@link InsetsSourceProvider} to be associated with this window, but only if the
-     * provider itself is controllable, as one window can be the provider of more than one inset
-     * type (i.e. gesture insets). If this window is controllable, all its animations must be
-     * controlled by its control target, and the visibility of this window should be taken account
-     * into the state of the control target.
-     *
-     * @param insetProvider the provider which should not be visible to the client.
-     * @see InsetsStateController#getInsetsForWindow(WindowState)
-     */
-    void setControllableInsetProvider(InsetsSourceProvider insetProvider) {
-        mControllableInsetProvider = insetProvider;
-    }
-
-    InsetsSourceProvider getControllableInsetProvider() {
-        return mControllableInsetProvider;
-    }
-
     private final class MoveAnimationSpec implements AnimationSpec {
 
         private final long mDuration;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2e801ab..e895d37 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2707,15 +2707,6 @@
             systemTheme.rebase();
         }
 
-        t.traceBegin("MakePowerManagerServiceReady");
-        try {
-            // TODO: use boot phase
-            mPowerManagerService.systemReady();
-        } catch (Throwable e) {
-            reportWtf("making Power Manager Service ready", e);
-        }
-        t.traceEnd();
-
         // Permission policy service
         t.traceBegin("StartPermissionPolicyService");
         mSystemServiceManager.startService(PermissionPolicyService.class);
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index f72b23c..2d082e1 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -63,6 +63,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
@@ -128,6 +129,19 @@
 
     private final PackageManager mPackageManager;
 
+    private static final String MIDI_LEGACY_STRING = "MIDI 1.0";
+    private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0";
+
+    // Used to lock mUsbMidiLegacyDeviceOpenCount and mUsbMidiUniversalDeviceInUse.
+    private final Object mUsbMidiLock = new Object();
+
+    // Number of times a USB MIDI 1.0 device has opened, based on the device name.
+    private final HashMap<String, Integer> mUsbMidiLegacyDeviceOpenCount =
+            new HashMap<String, Integer>();
+
+    // Whether a USB MIDI device has opened, based on the device name.
+    private final HashSet<String> mUsbMidiUniversalDeviceInUse = new HashSet<String>();
+
     // UID of BluetoothMidiService
     private int mBluetoothServiceUid;
 
@@ -271,6 +285,12 @@
             }
 
             for (DeviceConnection connection : mDeviceConnections.values()) {
+                if (connection.getDevice().getDeviceInfo().getType()
+                        == MidiDeviceInfo.TYPE_USB) {
+                    synchronized (mUsbMidiLock) {
+                        removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo());
+                    }
+                }
                 connection.getDevice().removeDeviceConnection(connection);
             }
         }
@@ -748,6 +768,7 @@
         synchronized (mDevicesByInfo) {
             for (Device device : mDevicesByInfo.values()) {
                 if (device.isUidAllowed(uid)) {
+                    deviceInfos.add(device.getDeviceInfo());
                     // UMP devices have protocols that are not PROTOCOL_UNKNOWN
                     if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
                         if (device.getDeviceInfo().getDefaultProtocol()
@@ -784,6 +805,15 @@
             }
         }
 
+        if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) {
+            synchronized (mUsbMidiLock) {
+                if (isUsbMidiDeviceInUseLocked(deviceInfo)) {
+                    throw new IllegalArgumentException("device already in use: " + deviceInfo);
+                }
+                addUsbMidiDeviceLocked(deviceInfo);
+            }
+        }
+
         // clear calling identity so bindService does not fail
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -1216,4 +1246,82 @@
         }
         pw.decreaseIndent();
     }
+
+    // hold mUsbMidiLock before calling this
+    private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return false;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        // Only one MIDI 2.0 device can be used at once.
+        // Multiple MIDI 1.0 devices can be used at once.
+        if (mUsbMidiUniversalDeviceInUse.contains(deviceName)
+                || ((tagName).equals(MIDI_UNIVERSAL_STRING)
+                && (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)))) {
+            return true;
+        }
+        return false;
+    }
+
+    // hold mUsbMidiLock before calling this
+    void addUsbMidiDeviceLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
+            mUsbMidiUniversalDeviceInUse.add(deviceName);
+        } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
+            int count = mUsbMidiLegacyDeviceOpenCount.getOrDefault(deviceName, 0) + 1;
+            mUsbMidiLegacyDeviceOpenCount.put(deviceName, count);
+        }
+    }
+
+    // hold mUsbMidiLock before calling this
+    void removeUsbMidiDeviceLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
+            mUsbMidiUniversalDeviceInUse.remove(deviceName);
+        } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
+            if (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)) {
+                int count = mUsbMidiLegacyDeviceOpenCount.get(deviceName);
+                if (count > 1) {
+                    mUsbMidiLegacyDeviceOpenCount.put(deviceName, count - 1);
+                } else {
+                    mUsbMidiLegacyDeviceOpenCount.remove(deviceName);
+                }
+            }
+        }
+    }
+
+    // The USB property name is in the form "manufacturer product#Id MIDI 1.0".
+    // This is defined in UsbDirectMidiDevice.java.
+    // This function extracts out the "manufacturer product#Id " part.
+    // Two devices would have the same device name if they had the following property name:
+    // "manufacturer product#Id MIDI 1.0"
+    // "manufacturer product#Id MIDI 2.0"
+    // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length.
+    String extractUsbDeviceName(String propertyName) {
+        return propertyName.substring(0, propertyName.length() - MIDI_LEGACY_STRING.length());
+    }
+
+    // The USB property name is in the form "manufacturer product#Id MIDI 1.0".
+    // This is defined in UsbDirectMidiDevice.java.
+    // This function extracts the "MIDI 1.0" part.
+    // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length.
+    String extractUsbDeviceTag(String propertyName) {
+        return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length());
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
index 0411b94..9cf6c03 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
@@ -263,7 +263,7 @@
     @Test
     public void testUserActivityOnDeviceStateChange() {
         createService();
-        mService.systemReady();
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         final DisplayInfo info = new DisplayInfo();
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
index 64e8613..8b7cc74 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -18,8 +18,8 @@
 
 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
 import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.HARDWARE;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.SOFTWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -102,106 +102,106 @@
     public void testMigration1() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE1);
 
-        assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
-        assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     @Test
     public void testMigration2() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE2);
 
-        assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
-        assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertTrue(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
-        assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 11, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 11, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 11, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 11, CAMERA));
 
-        assertTrue(ps.getState(SOFTWARE, 12, MICROPHONE).isEnabled());
-        assertNull(ps.getState(SOFTWARE, 12, CAMERA));
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 12, MICROPHONE).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 12, CAMERA));
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
-        assertNull(ps.getState(HARDWARE, 11, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 11, CAMERA));
-        assertNull(ps.getState(HARDWARE, 12, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 12, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 11, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 11, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 12, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 12, CAMERA));
     }
 
     @Test
     public void testMigration3() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE3);
 
-        assertFalse(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
-        assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     @Test
     public void testMigration4() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE4);
 
-        assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
-        assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertFalse(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     @Test
     public void testMigration5() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE5);
 
-        assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
-        assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA).isEnabled());
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     @Test
     public void testMigration6() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE6);
 
-        assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA));
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     private PersistedState migrateFromFile(String fileName) throws IOException {
@@ -233,32 +233,32 @@
     public void testPersistence1Version2() throws IOException {
         PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE7);
 
-        assertEquals(1, ps.getState(SOFTWARE, 0, MICROPHONE).getState());
-        assertEquals(123L, ps.getState(SOFTWARE, 0, MICROPHONE).getLastChange());
-        assertEquals(2, ps.getState(SOFTWARE, 0, CAMERA).getState());
-        assertEquals(123L, ps.getState(SOFTWARE, 0, CAMERA).getLastChange());
+        assertEquals(1, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).getState());
+        assertEquals(123L, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).getLastChange());
+        assertEquals(2, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).getState());
+        assertEquals(123L, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).getLastChange());
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
     }
 
     @Test
     public void testPersistence2Version2() throws IOException {
         PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE8);
 
-        assertEquals(1, ps.getState(HARDWARE, 0, MICROPHONE).getState());
-        assertEquals(1234L, ps.getState(HARDWARE, 0, MICROPHONE).getLastChange());
-        assertEquals(2, ps.getState(HARDWARE, 0, CAMERA).getState());
-        assertEquals(1234L, ps.getState(HARDWARE, 0, CAMERA).getLastChange());
+        assertEquals(1, ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE).getState());
+        assertEquals(1234L, ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE).getLastChange());
+        assertEquals(2, ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA).getState());
+        assertEquals(1234L, ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA).getLastChange());
 
-        assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 0, CAMERA));
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     private PersistedState getPersistedStateV2(String version2FilePath) throws IOException {
@@ -296,13 +296,15 @@
             SensorPrivacyStateController sensorPrivacyStateController =
                     getSensorPrivacyStateControllerImpl();
 
-            SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
-            SensorState camState = sensorPrivacyStateController.getState(SOFTWARE, 0, CAMERA);
+            SensorState micState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+                    MICROPHONE);
+            SensorState camState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+                    CAMERA);
 
             assertEquals(SensorPrivacyManager.StateTypes.DISABLED, micState.getState());
             assertEquals(SensorPrivacyManager.StateTypes.DISABLED, camState.getState());
-            verify(persistedState, times(1)).getState(SOFTWARE, 0, MICROPHONE);
-            verify(persistedState, times(1)).getState(SOFTWARE, 0, CAMERA);
+            verify(persistedState, times(1)).getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE);
+            verify(persistedState, times(1)).getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA);
         } finally {
             mockitoSession.finishMocking();
         }
@@ -319,14 +321,16 @@
             PersistedState persistedState = mock(PersistedState.class);
             SensorState sensorState = mock(SensorState.class);
             doReturn(persistedState).when(() -> PersistedState.fromFile(any()));
-            doReturn(sensorState).when(persistedState).getState(SOFTWARE, 0, MICROPHONE);
+            doReturn(sensorState).when(persistedState).getState(TOGGLE_TYPE_SOFTWARE, 0,
+                    MICROPHONE);
             doReturn(SensorPrivacyManager.StateTypes.ENABLED).when(sensorState).getState();
             doReturn(0L).when(sensorState).getLastChange();
 
             SensorPrivacyStateController sensorPrivacyStateController =
                     getSensorPrivacyStateControllerImpl();
 
-            SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
+            SensorState micState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+                    MICROPHONE);
 
             assertEquals(SensorPrivacyManager.StateTypes.ENABLED, micState.getState());
             assertEquals(0L, micState.getLastChange());
@@ -349,13 +353,13 @@
             SensorPrivacyStateController sensorPrivacyStateController =
                     getSensorPrivacyStateControllerImpl();
 
-            sensorPrivacyStateController.setState(SOFTWARE, 0, MICROPHONE, true,
+            sensorPrivacyStateController.setState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE, true,
                     mock(Handler.class), changed -> {});
 
             ArgumentCaptor<SensorState> captor = ArgumentCaptor.forClass(SensorState.class);
 
-            verify(persistedState, times(1)).setState(eq(SOFTWARE), eq(0), eq(MICROPHONE),
-                    captor.capture());
+            verify(persistedState, times(1)).setState(eq(TOGGLE_TYPE_SOFTWARE), eq(0),
+                    eq(MICROPHONE), captor.capture());
             assertEquals(SensorPrivacyManager.StateTypes.ENABLED, captor.getValue().getState());
         } finally {
             mockitoSession.finishMocking();
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 3890d4d..897b91e 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -37,7 +37,7 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
-import android.attention.AttentionManagerInternal.ProximityCallbackInternal;
+import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.IBinder;
@@ -48,7 +48,7 @@
 import android.provider.DeviceConfig;
 import android.service.attention.IAttentionCallback;
 import android.service.attention.IAttentionService;
-import android.service.attention.IProximityCallback;
+import android.service.attention.IProximityUpdateCallback;
 
 import androidx.test.filters.SmallTest;
 
@@ -85,7 +85,7 @@
     @Mock
     Context mContext;
     @Mock
-    private ProximityCallbackInternal mMockProximityCallbackInternal;
+    private ProximityUpdateCallbackInternal mMockProximityUpdateCallbackInternal;
 
     @Before
     public void setUp() throws RemoteException {
@@ -119,7 +119,8 @@
     public void testRegisterProximityUpdates_returnFalseWhenServiceDisabled() {
         mSpyAttentionManager.mIsServiceEnabled = false;
 
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isFalse();
     }
 
@@ -128,7 +129,8 @@
         mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(false).when(mSpyAttentionManager).isServiceAvailable();
 
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isFalse();
     }
 
@@ -139,7 +141,8 @@
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(false).when(mMockIPowerManager).isInteractive();
 
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isFalse();
     }
 
@@ -149,9 +152,10 @@
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
 
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isTrue();
-        verify(mMockProximityCallbackInternal, times(1))
+        verify(mMockProximityUpdateCallbackInternal, times(1))
                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
     }
 
@@ -161,21 +165,23 @@
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
 
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isTrue();
 
         ProximityUpdate prevProximityUpdate = mSpyAttentionManager.mCurrentProximityUpdate;
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isTrue();
         assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isEqualTo(prevProximityUpdate);
-        verify(mMockProximityCallbackInternal, times(1))
+        verify(mMockProximityUpdateCallbackInternal, times(1))
                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
     }
 
     @Test
     public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
-        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
-        verifyZeroInteractions(mMockProximityCallbackInternal);
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
+        verifyZeroInteractions(mMockProximityUpdateCallbackInternal);
     }
 
     @Test
@@ -184,11 +190,11 @@
         mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
-        mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
-        verify(mMockProximityCallbackInternal, times(1))
+        mSpyAttentionManager.onStartProximityUpdates(mMockProximityUpdateCallbackInternal);
+        verify(mMockProximityUpdateCallbackInternal, times(1))
                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
 
-        ProximityCallbackInternal mismatchedCallback = new ProximityCallbackInternal() {
+        ProximityUpdateCallbackInternal mismatchedCallback = new ProximityUpdateCallbackInternal() {
             @Override
             public void onProximityUpdate(double distance) {
                 fail("Callback shouldn't have responded.");
@@ -196,7 +202,7 @@
         };
         mSpyAttentionManager.onStopProximityUpdates(mismatchedCallback);
 
-        verifyNoMoreInteractions(mMockProximityCallbackInternal);
+        verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
     }
 
     @Test
@@ -205,8 +211,8 @@
         mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
-        mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
-        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
+        mSpyAttentionManager.onStartProximityUpdates(mMockProximityUpdateCallbackInternal);
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
 
         assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isNull();
     }
@@ -217,14 +223,14 @@
         mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
-        mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
-        verify(mMockProximityCallbackInternal, times(1))
+        mSpyAttentionManager.onStartProximityUpdates(mMockProximityUpdateCallbackInternal);
+        verify(mMockProximityUpdateCallbackInternal, times(1))
                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
 
         // Attention Service unregisters the proximity update twice in a row.
-        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
-        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
-        verifyNoMoreInteractions(mMockProximityCallbackInternal);
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
+        verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
     }
 
     @Test
@@ -337,7 +343,8 @@
         public void cancelAttentionCheck(IAttentionCallback callback) {
         }
 
-        public void onStartProximityUpdates(IProximityCallback callback) throws RemoteException {
+        public void onStartProximityUpdates(IProximityUpdateCallback callback)
+                throws RemoteException {
             callback.onProximityUpdate(PROXIMITY_SUCCESS_STATE);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 2ae2854..9aac81c 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -361,6 +361,16 @@
     }
 
     @Test
+    public void close_cleanVirtualAudioController() {
+        mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+        mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mCallback);
+
+        mDeviceImpl.close();
+
+        assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNull();
+    }
+
+    @Test
     public void sendKeyEvent_noFd() {
         assertThrows(
                 IllegalArgumentException.class,
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 ed7aac7..1b92017 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -321,7 +321,7 @@
     }
 
     private void startSystem() {
-        mService.systemReady();
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
         // Grab the BatteryReceiver
         ArgumentCaptor<BatteryReceiver> batCaptor = ArgumentCaptor.forClass(BatteryReceiver.class);
@@ -439,7 +439,7 @@
     @Test
     public void testGetDesiredScreenPolicy_WithVR() {
         createService();
-        mService.systemReady();
+        startSystem();
         // Brighten up the screen
         mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0,
                 null, null);
@@ -627,8 +627,7 @@
     public void testWasDeviceIdleFor_true() {
         int interval = 1000;
         createService();
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
         mService.onUserActivity();
         advanceTime(interval + 1 /* just a little more */);
         assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue();
@@ -638,8 +637,7 @@
     public void testWasDeviceIdleFor_false() {
         int interval = 1000;
         createService();
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
         mService.onUserActivity();
         assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
     }
@@ -647,8 +645,7 @@
     @Test
     public void testForceSuspend_putsDeviceToSleep() {
         createService();
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
 
         // Verify that we start awake
         assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -693,8 +690,7 @@
         //
         // TEST STARTS HERE
         //
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
 
         // Verify that we start awake
         assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -792,7 +788,7 @@
         createService();
         assertTrue(isAcquired[0]);
 
-        mService.systemReady();
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
         assertTrue(isAcquired[0]);
 
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -1058,23 +1054,6 @@
         assertThat(mService.getGlobalWakefulnessLocked()).isNotEqualTo(WAKEFULNESS_ASLEEP);
     }
 
-
-    @SuppressWarnings("GuardedBy")
-    @Test
-    public void testInattentiveSleep_goesToSleepFromDream() throws Exception {
-        setAttentiveTimeout(20000);
-        createService();
-        startSystem();
-        setPluggedIn(true);
-        forceAwake();
-        forceDream();
-        when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
-        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
-
-        advanceTime(20500);
-        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
-    }
-
     @Test
     public void testWakeLock_affectsProperDisplayGroup() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
@@ -1248,7 +1227,7 @@
     public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
-        mService.systemReady();
+        startSystem();
 
         mService.getBinderServiceInstance().wakeUp(mClock.now(),
                 PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
@@ -1461,7 +1440,7 @@
     @Test
     public void testSetPowerBoost_redirectsCallToNativeWrapper() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         mService.getBinderServiceInstance().setPowerBoost(Boost.INTERACTION, 1234);
 
@@ -1471,7 +1450,7 @@
     @Test
     public void testSetPowerMode_redirectsCallToNativeWrapper() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         // Enabled launch boost in BatterySaverController to allow setting launch mode.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(false);
@@ -1487,7 +1466,7 @@
     @Test
     public void testSetPowerMode_withLaunchBoostDisabledAndModeLaunch_ignoresCallToEnable() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         // Disables launch boost in BatterySaverController.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1503,7 +1482,7 @@
     @Test
     public void testSetPowerModeChecked_returnsNativeCallResult() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         // Disables launch boost in BatterySaverController.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1666,7 +1645,7 @@
     @Test
     public void testGetFullPowerSavePolicy_returnsStateMachineResult() {
         createService();
-        mService.systemReady();
+        startSystem();
         BatterySaverPolicyConfig mockReturnConfig = new BatterySaverPolicyConfig.Builder().build();
         when(mBatterySaverStateMachineMock.getFullBatterySaverPolicy())
                 .thenReturn(mockReturnConfig);
@@ -1681,7 +1660,7 @@
     @Test
     public void testSetFullPowerSavePolicy_callsStateMachine() {
         createService();
-        mService.systemReady();
+        startSystem();
         BatterySaverPolicyConfig mockSetPolicyConfig =
                 new BatterySaverPolicyConfig.Builder().build();
         when(mBatterySaverStateMachineMock.setFullBatterySaverPolicy(any())).thenReturn(true);
@@ -1695,7 +1674,7 @@
     @Test
     public void testLowPowerStandby_whenInactive_FgsWakeLockEnabled() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
         mService.setDeviceIdleModeInternal(true);
@@ -1706,7 +1685,7 @@
     @Test
     public void testLowPowerStandby_whenActive_FgsWakeLockDisabled() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
         mService.setDeviceIdleModeInternal(true);
@@ -1718,7 +1697,7 @@
     @Test
     public void testLowPowerStandby_whenActive_FgsWakeLockEnabledIfAllowlisted() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
         mService.setDeviceIdleModeInternal(true);
@@ -1731,7 +1710,7 @@
     @Test
     public void testLowPowerStandby_whenActive_BoundTopWakeLockDisabled() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("BoundTopWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_BOUND_TOP);
         mService.setDeviceIdleModeInternal(true);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 020d9f8..e1a4989 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -100,7 +100,7 @@
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock
-    private VibrationThread.VibrationCallbacks mThreadCallbacks;
+    private VibrationThread.VibratorManagerHooks mManagerHooks;
     @Mock
     private VibratorController.OnVibrationCompleteListener mControllerCallbacks;
     @Mock
@@ -648,9 +648,9 @@
                 VibrationEffect.createOneShot(10, 100)));
 
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
-        verify(mThreadCallbacks, never()).prepareSyncedVibration(anyLong(), any());
-        verify(mThreadCallbacks, never()).triggerSyncedVibration(anyLong());
-        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+        verify(mManagerHooks, never()).prepareSyncedVibration(anyLong(), any());
+        verify(mManagerHooks, never()).triggerSyncedVibration(anyLong());
+        verify(mManagerHooks, never()).cancelSyncedVibration();
     }
 
     @Test
@@ -807,8 +807,8 @@
         mockVibrators(vibratorIds);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
         mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
-        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true);
-        when(mThreadCallbacks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
+        when(mManagerHooks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true);
+        when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
 
         VibrationEffect composed = VibrationEffect.startComposition()
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100)
@@ -823,9 +823,9 @@
         waitForCompletion(thread);
 
         long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_COMPOSE;
-        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
-        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+        verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks, never()).cancelSyncedVibration();
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
 
         VibrationEffectSegment expected = expectedPrimitive(
@@ -840,8 +840,8 @@
         mockVibrators(vibratorIds);
         mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
         mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
-        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
-        when(mThreadCallbacks.triggerSyncedVibration(anyLong())).thenReturn(true);
+        when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
+        when(mManagerHooks.triggerSyncedVibration(anyLong())).thenReturn(true);
 
         long vibrationId = 1;
         VibrationEffect composed = VibrationEffect.startComposition()
@@ -863,9 +863,9 @@
                 | IVibratorManager.CAP_MIXED_TRIGGER_ON
                 | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM
                 | IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
-        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
-        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+        verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks, never()).cancelSyncedVibration();
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
     }
 
@@ -875,7 +875,7 @@
         mockVibrators(vibratorIds);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
-        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(false);
+        when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(false);
 
         long vibrationId = 1;
         CombinedVibration effect = CombinedVibration.startParallel()
@@ -886,9 +886,9 @@
         waitForCompletion(thread);
 
         long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_ON;
-        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mThreadCallbacks, never()).triggerSyncedVibration(eq(vibrationId));
-        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+        verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mManagerHooks, never()).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks, never()).cancelSyncedVibration();
 
         assertEquals(Arrays.asList(expectedOneShot(10)),
                 mVibratorProviders.get(1).getEffectSegments());
@@ -903,8 +903,8 @@
         int[] vibratorIds = new int[]{1, 2};
         mockVibrators(vibratorIds);
         mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
-        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
-        when(mThreadCallbacks.triggerSyncedVibration(anyLong())).thenReturn(false);
+        when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
+        when(mManagerHooks.triggerSyncedVibration(anyLong())).thenReturn(false);
 
         long vibrationId = 1;
         CombinedVibration effect = CombinedVibration.startParallel()
@@ -919,9 +919,9 @@
                 | IVibratorManager.CAP_PREPARE_PERFORM
                 | IVibratorManager.CAP_MIXED_TRIGGER_ON
                 | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
-        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
-        verify(mThreadCallbacks).cancelSyncedVibration();
+        verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks).cancelSyncedVibration();
         assertTrue(mVibratorProviders.get(1).getAmplitudes().isEmpty());
     }
 
@@ -1161,9 +1161,9 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
 
         // Vibration completed but vibrator not yet released.
-        verify(mThreadCallbacks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
+        verify(mManagerHooks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
                 eq(Vibration.Status.FINISHED));
-        verify(mThreadCallbacks, never()).onVibratorsReleased();
+        verify(mManagerHooks, never()).onVibrationThreadReleased();
 
         // Thread still running ramp down.
         assertTrue(thread.isAlive());
@@ -1177,9 +1177,9 @@
         waitForCompletion(thread);
 
         // Does not cancel already finished vibration, but releases vibrator.
-        verify(mThreadCallbacks, never()).onVibrationCompleted(eq(vibrationId),
+        verify(mManagerHooks, never()).onVibrationCompleted(eq(vibrationId),
                 eq(Vibration.Status.CANCELLED));
-        verify(mThreadCallbacks).onVibratorsReleased();
+        verify(mManagerHooks).onVibrationThreadReleased();
     }
 
     @Test
@@ -1300,7 +1300,7 @@
 
     private VibrationThread startThreadAndDispatcher(Vibration vib) {
         VibrationThread thread = new VibrationThread(vib, mVibrationSettings, mEffectAdapter,
-                createVibratorControllers(), mWakeLock, mIBatteryStatsMock, mThreadCallbacks);
+                createVibratorControllers(), mWakeLock, mIBatteryStatsMock, mManagerHooks);
         doAnswer(answer -> {
             thread.vibratorComplete(answer.getArgument(0));
             return null;
@@ -1380,8 +1380,8 @@
     }
 
     private void verifyCallbacksTriggered(long vibrationId, Vibration.Status expectedStatus) {
-        verify(mThreadCallbacks).onVibrationCompleted(eq(vibrationId), eq(expectedStatus));
-        verify(mThreadCallbacks).onVibratorsReleased();
+        verify(mManagerHooks).onVibrationCompleted(eq(vibrationId), eq(expectedStatus));
+        verify(mManagerHooks).onVibrationThreadReleased();
     }
 
     private final class TestLooperAutoDispatcher extends Thread {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index 184ea52..fe1ea0d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -33,6 +33,7 @@
 import android.app.ActivityOptions;
 import android.app.Instrumentation;
 import android.app.Instrumentation.ActivityMonitor;
+import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -40,6 +41,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
+import android.util.Rational;
 import android.view.SurfaceControl;
 import android.window.TaskOrganizer;
 
@@ -91,6 +93,20 @@
     }
 
     @Test
+    public void testMakeLaunchIntoPip() {
+        // Construct some params with set values
+        PictureInPictureParams params = new PictureInPictureParams.Builder()
+                .setAspectRatio(new Rational(1, 1))
+                .build();
+        // Construct ActivityOptions via makeLaunchIntoPip
+        ActivityOptions opts = ActivityOptions.makeLaunchIntoPip(params);
+
+        // Verify the params in ActivityOptions has the right flag being turned on
+        assertNotNull(opts.getLaunchIntoPipParams());
+        assertTrue(opts.isLaunchIntoPip());
+    }
+
+    @Test
     public void testTransferLaunchCookie() {
         final Binder cookie = new Binder();
         final ActivityOptions options = ActivityOptions.makeBasic();
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 043bc07..ffc10d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -109,6 +109,7 @@
 
 import android.app.ActivityOptions;
 import android.app.ICompatCameraControlCallback;
+import android.app.PictureInPictureParams;
 import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.DestroyActivityItem;
@@ -2200,6 +2201,20 @@
         assertFalse(activity.supportsPictureInPicture());
     }
 
+    @Test
+    public void testLaunchIntoPip() {
+        final PictureInPictureParams params = new PictureInPictureParams.Builder()
+                .build();
+        final ActivityOptions opts = ActivityOptions.makeLaunchIntoPip(params);
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setLaunchIntoPipActivityOptions(opts)
+                .build();
+
+        // Verify the pictureInPictureArgs is set on the new Activity
+        assertNotNull(activity.pictureInPictureArgs);
+        assertTrue(activity.pictureInPictureArgs.isLaunchIntoPip());
+    }
+
     private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
             boolean shouldUpdate, boolean activityChange) {
         reset(activity.app);
@@ -3044,7 +3059,7 @@
         mDisplayContent.setImeLayeringTarget(app);
         mDisplayContent.updateImeInputAndControlTarget(app);
 
-        InsetsState state = mDisplayContent.getInsetsPolicy().getInsetsForWindow(app);
+        InsetsState state = app.getInsetsState();
         assertFalse(state.getSource(ITYPE_IME).isVisible());
         assertTrue(state.getSource(ITYPE_IME).getFrame().isEmpty());
 
@@ -3064,7 +3079,7 @@
 
         // Verify when IME is visible and the app can receive the right IME insets from policy.
         makeWindowVisibleAndDrawn(app, mImeWindow);
-        state = mDisplayContent.getInsetsPolicy().getInsetsForWindow(app);
+        state = app.getInsetsState();
         assertTrue(state.getSource(ITYPE_IME).isVisible());
         assertEquals(state.getSource(ITYPE_IME).getFrame(), imeSource.getFrame());
     }
@@ -3076,7 +3091,7 @@
         final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
         final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
 
-        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindow(
+        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindowContainer(
                 mImeWindow, null, null);
         mImeWindow.getControllableInsetProvider().setServerVisible(true);
 
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 c8e48a4..87abc53 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -39,6 +39,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.os.Process.SYSTEM_UID;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -68,6 +69,7 @@
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
+import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -1141,6 +1143,34 @@
     }
 
     @Test
+    public void testStartActivityInner_inTaskFragment_allowedForSystemUid() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+                true /* createdByOrganizer */);
+        sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+        taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class), SYSTEM_UID,
+                "system_uid");
+
+        starter.startActivityInner(
+                /* r */targetRecord,
+                /* sourceRecord */ sourceRecord,
+                /* voiceSession */null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */true,
+                /* options */null,
+                /* inTask */null,
+                /* inTaskFragment */ taskFragment,
+                /* restrictedBgActivity */false,
+                /* intentGrants */null);
+
+        assertTrue(taskFragment.hasChild());
+    }
+
+    @Test
     public void testStartActivityInner_inTaskFragment_allowedForSameUid() {
         final ActivityStarter starter = prepareStarter(0, false);
         final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
@@ -1264,4 +1294,36 @@
         // Verify the cookie is updated
         assertTrue(mRootWindowContainer.topRunningActivity().mLaunchCookie == newCookie);
     }
+
+    @Test
+    public void testStartLaunchIntoPipActivity() {
+        final ActivityStarter starter = prepareStarter(0, false);
+
+        // Create an activity from ActivityOptions#makeLaunchIntoPip
+        final PictureInPictureParams params = new PictureInPictureParams.Builder()
+                .build();
+        final ActivityOptions opts = ActivityOptions.makeLaunchIntoPip(params);
+        ActivityRecord targetRecord = new ActivityBuilder(mAtm)
+                .setLaunchIntoPipActivityOptions(opts)
+                .build();
+
+        // Start the target launch-into-pip activity from a source
+        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        starter.startActivityInner(
+                /* r */ targetRecord,
+                /* sourceRecord */ sourceRecord,
+                /* voiceSession */ null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */ true,
+                /* options */ opts,
+                /* inTask */ null,
+                /* inTaskFragment */ null,
+                /* restrictedBgActivity */ false,
+                /* intentGrants */ null);
+
+        // Verify the ActivityRecord#getLaunchIntoPipHostActivity points to sourceRecord.
+        assertThat(targetRecord.getLaunchIntoPipHostActivity()).isNotNull();
+        assertEquals(targetRecord.getLaunchIntoPipHostActivity(), sourceRecord);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 497ae1d..db22757 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -258,11 +258,9 @@
                 .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
         mWindow.mAboveInsetsState.set(
                 mDisplayContent.getInsetsStateController().getRawInsetsState());
-        final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
-                .getSource(ITYPE_STATUS_BAR).getFrame();
+        final Rect frame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
         mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
-        final Rect rotatedFrame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
-                .getSource(ITYPE_STATUS_BAR).getFrame();
+        final Rect rotatedFrame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
 
         assertEquals(DISPLAY_WIDTH, frame.width());
         assertEquals(DISPLAY_HEIGHT, rotatedFrame.width());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index 5f96267..ca8481a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -71,7 +71,7 @@
     public void testIsImeShowing() {
         WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
         makeWindowVisibleAndDrawn(ime);
-        mImeProvider.setWindow(ime, null, null);
+        mImeProvider.setWindowContainer(ime, null, null);
 
         WindowState target = createWindow(null, TYPE_APPLICATION, "app");
         mDisplayContent.setImeLayeringTarget(target);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 2987f94..c61b88b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -63,7 +63,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         statusBar.getFrame().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.onPostLayout();
         assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
         assertEquals(Insets.of(0, 100, 0, 0),
@@ -80,7 +80,7 @@
         ime.mGivenContentInsets.set(0, 0, 0, 60);
         ime.mGivenVisibleInsets.set(0, 0, 0, 75);
         ime.mHasSurface = true;
-        mProvider.setWindow(ime, null, null);
+        mProvider.setWindowContainer(ime, null, null);
         mProvider.onPostLayout();
         assertEquals(new Rect(0, 0, 500, 40), mProvider.getSource().getFrame());
         assertEquals(new Rect(0, 0, 500, 25), mProvider.getSource().getVisibleFrame());
@@ -95,10 +95,10 @@
     public void testPostLayout_invisible() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         statusBar.getFrame().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.onPostLayout();
         assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
-                        false /* ignoreVisibility */));
+                false /* ignoreVisibility */));
     }
 
     @Test
@@ -106,7 +106,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         statusBar.getFrame().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
-        mProvider.setWindow(statusBar,
+        mProvider.setWindowContainer(statusBar,
                 (displayFrames, windowState, rect) -> {
                     rect.set(10, 10, 20, 20);
                 }, null);
@@ -126,7 +126,7 @@
         assertNull(mProvider.getControlTarget());
 
         // We can have the control or the control target after we have the insets source window.
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.updateControlForTarget(target, false /* force */);
         assertNotNull(mProvider.getControl(target));
         assertNotNull(mProvider.getControlTarget());
@@ -164,7 +164,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrame().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.updateControlForFakeTarget(target);
         assertNotNull(mProvider.getControl(target));
         assertNull(mProvider.getControl(target).getLeash());
@@ -178,7 +178,7 @@
 
         inputMethod.getFrame().set(new Rect(0, 400, 500, 500));
 
-        mImeProvider.setWindow(inputMethod, null, null);
+        mImeProvider.setWindowContainer(inputMethod, null, null);
         mImeProvider.setServerVisible(false);
         mImeSource.setVisible(true);
         mImeProvider.updateSourceFrame();
@@ -201,7 +201,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrame().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.updateControlForTarget(target, false /* force */);
         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
         requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
@@ -215,7 +215,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrame().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
         requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
         target.setRequestedVisibilities(requestedVisibilities);
@@ -228,7 +228,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         statusBar.getFrame().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.onPostLayout();
         assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
         // Still apply top insets if window overlaps even if it's top doesn't exactly match
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 2eece4c..c7a1b07 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -68,11 +68,14 @@
         // IME cannot be the IME target.
         ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-        assertNull(getController().getInsetsForWindow(navBar).peekSource(ITYPE_IME));
-        assertNull(getController().getInsetsForWindow(navBar).peekSource(ITYPE_STATUS_BAR));
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null);
+
+        assertNull(navBar.getInsetsState().peekSource(ITYPE_IME));
+        assertNull(navBar.getInsetsState().peekSource(ITYPE_STATUS_BAR));
     }
 
     @Test
@@ -81,13 +84,15 @@
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
         app.setWindowingMode(WINDOWING_MODE_PINNED);
 
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_IME));
+        assertNull(app.getInsetsState().peekSource(ITYPE_STATUS_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_IME));
     }
 
     @Test
@@ -96,12 +101,14 @@
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
         app.setWindowingMode(WINDOWING_MODE_FREEFORM);
 
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_STATUS_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR));
     }
 
     @Test
@@ -110,19 +117,21 @@
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
         app.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         app.setAlwaysOnTop(true);
 
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_STATUS_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR));
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
     @Test
     public void testStripForDispatch_independentSources() {
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
 
         final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
         final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
@@ -130,34 +139,34 @@
         app1.mAboveInsetsState.addSource(getController().getRawInsetsState().getSource(ITYPE_IME));
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME)
+        assertFalse(app2.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
-        assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME)
+        assertTrue(app1.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
     @Test
     public void testStripForDispatch_belowIme() {
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true);
         app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
     @Test
     public void testStripForDispatch_aboveIme() {
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
+        assertFalse(app.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
     }
 
@@ -172,7 +181,7 @@
 
         // Make IME and stay visible during the test.
         mImeWindow.setHasSurface(true);
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
         getController().onImeControlTargetChanged(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
         requestedVisibilities.setVisibility(ITYPE_IME, true);
@@ -195,7 +204,7 @@
 
         // app won't get visible IME insets while above IME even when IME is visible.
         assertTrue(getController().getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
-        assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
+        assertFalse(app.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
 
         // Reset invocation counter.
@@ -212,13 +221,13 @@
         verify(app, atLeastOnce()).notifyInsetsChanged();
 
         // app will get visible IME insets while below IME.
-        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
     @Test
     public void testStripForDispatch_childWindow_altFocusable() {
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
@@ -231,15 +240,15 @@
         mDisplayContent.applySurfaceChangesTransaction();
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
-        assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
+        assertFalse(child.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
     @Test
     public void testStripForDispatch_childWindow_splitScreen() {
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
@@ -252,8 +261,8 @@
         mDisplayContent.applySurfaceChangesTransaction();
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
-        assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
+        assertFalse(child.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
     }
 
@@ -267,14 +276,14 @@
 
         InsetsSourceProvider statusBarProvider =
                 getController().getSourceProvider(ITYPE_STATUS_BAR);
-        statusBarProvider.setWindow(statusBar, null, ((displayFrames, windowState, rect) ->
-                        rect.set(0, 1, 2, 3)));
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+        statusBarProvider.setWindowContainer(statusBar, null, ((displayFrames, windowState, rect) ->
+                rect.set(0, 1, 2, 3)));
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null);
         statusBar.setControllableInsetProvider(statusBarProvider);
 
         statusBarProvider.onPostLayout();
 
-        final InsetsState state = getController().getInsetsForWindow(ime);
+        final InsetsState state = ime.getInsetsState();
         assertEquals(new Rect(0, 1, 2, 3), state.getSource(ITYPE_STATUS_BAR).getFrame());
     }
 
@@ -285,10 +294,14 @@
         final WindowState climateBar = createWindow(null, TYPE_APPLICATION, "climateBar");
         final WindowState extraNavBar = createWindow(null, TYPE_APPLICATION, "extraNavBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
-        getController().getSourceProvider(ITYPE_CLIMATE_BAR).setWindow(climateBar, null, null);
-        getController().getSourceProvider(ITYPE_EXTRA_NAVIGATION_BAR).setWindow(extraNavBar, null,
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_CLIMATE_BAR).setWindowContainer(climateBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_EXTRA_NAVIGATION_BAR).setWindowContainer(
+                extraNavBar, null,
                 null);
         getController().onBarControlTargetChanged(app, null, app, null);
         InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
@@ -299,7 +312,8 @@
     public void testControlRevoked() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
         getController().onBarControlTargetChanged(app, null, null, null);
         assertNotNull(getController().getControlsForDispatch(app));
         getController().onBarControlTargetChanged(null, null, null, null);
@@ -310,7 +324,8 @@
     public void testControlRevoked_animation() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
         getController().onBarControlTargetChanged(app, null, null, null);
         assertNotNull(getController().getControlsForDispatch(app));
         statusBar.cancelAnimation();
@@ -322,7 +337,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         final InsetsSourceProvider provider = getController().getSourceProvider(ITYPE_STATUS_BAR);
-        provider.setWindow(statusBar, null, null);
+        provider.setWindowContainer(statusBar, null, null);
 
         final InsetsState rotatedState = new InsetsState(app.getInsetsState(),
                 true /* copySources */);
@@ -344,7 +359,8 @@
         final WindowState statusBar = createTestWindow("statusBar");
         final WindowState navBar = createTestWindow("navBar");
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
 
         assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
@@ -365,8 +381,10 @@
         final WindowState statusBar = createTestWindow("statusBar");
         final WindowState navBar = createTestWindow("navBar");
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
 
         assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
@@ -386,10 +404,12 @@
         final WindowState statusBar = createTestWindow("statusBar");
         final WindowState navBar = createTestWindow("navBar");
 
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null);
         getController().getSourceProvider(ITYPE_IME).setClientVisible(true);
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
         getController().updateAboveInsetsState(ime, false /* notifyInsetsChange */);
         getController().updateAboveInsetsState(statusBar, false /* notifyInsetsChange */);
         getController().updateAboveInsetsState(navBar, false /* notifyInsetsChange */);
@@ -420,11 +440,12 @@
     @Test
     public void testDispatchGlobalInsets() {
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR));
         app.mAttrs.receiveInsetsIgnoringZOrder = true;
-        assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNotNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR));
     }
 
     private WindowState createTestWindow(String name) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index ee17f52..ba65104 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -251,7 +251,8 @@
         ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity);
 
         // Move first activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity,
+                null /* launchIntoPipHostActivity */, "initialMove");
 
         final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
         Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
@@ -260,7 +261,8 @@
         ensureTaskPlacement(fullscreenTask, secondActivity);
 
         // Move second activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity,
+                null /* launchIntoPipHostActivity */, "secondMove");
 
         // Need to get root tasks again as a new instance might have been created.
         pinnedRootTask = taskDisplayArea.getRootPinnedTask();
@@ -291,7 +293,8 @@
 
 
         // Move first activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity,
+                null /* launchIntoPipHostActivity */, "initialMove");
 
         assertTrue(firstActivity.mRequestForceTransition);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index b815c38..7b38a95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -31,6 +31,7 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
@@ -2128,6 +2129,136 @@
                 APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE);
     }
 
+    @Test
+    public void testIsEligibleForLetterboxEducation_educationNotEnabled_returnsFalse() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(false);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_notEligibleForFixedOrientation_returnsFalse() {
+        setUpDisplaySizeWithApp(1000, 2500);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_windowingModeMultiWindow_returnsFalse() {
+        // Support non resizable in multi window
+        mAtm.mDevEnableNonResizableMultiWindow = true;
+        setUpDisplaySizeWithApp(1000, 1200);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+        final TestSplitOrganizer organizer =
+                new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+        // Non-resizable landscape activity
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        final Rect originalBounds = new Rect(mActivity.getBounds());
+
+        // Move activity to split screen which takes half of the screen.
+        mTask.reparent(organizer.mPrimary, POSITION_TOP,
+                false /*moveParents*/, "test");
+        organizer.mPrimary.setBounds(0, 0, 1000, 600);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_fixedOrientationLandscape_returnsFalse() {
+        setUpDisplaySizeWithApp(1000, 2500);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_hasStartingWindow_returnsFalseUntilRemoved() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mStartingData = mock(StartingData.class);
+        mActivity.attachStartingWindow(
+                createWindowState(new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING),
+                        mActivity));
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+
+        // Verify that after removing the starting window isEligibleForLetterboxEducation returns
+        // true and mTask.dispatchTaskInfoChangedIfNeeded is called.
+        spyOn(mTask);
+        mActivity.removeStartingWindow();
+
+        assertTrue(mActivity.isEligibleForLetterboxEducation());
+        verify(mTask).dispatchTaskInfoChangedIfNeeded(true);
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_hasStartingWindowAndEducationNotEnabled() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(false);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mStartingData = mock(StartingData.class);
+        mActivity.attachStartingWindow(
+                createWindowState(new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING),
+                        mActivity));
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+
+        // Verify that after removing the starting window isEligibleForLetterboxEducation still
+        // returns false and mTask.dispatchTaskInfoChangedIfNeeded isn't called.
+        spyOn(mTask);
+        mActivity.removeStartingWindow();
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+        verify(mTask, never()).dispatchTaskInfoChangedIfNeeded(true);
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_letterboxedForFixedOrientation_returnsTrue() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        assertTrue(mActivity.isEligibleForLetterboxEducation());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_sizeCompatAndEligibleForFixedOrientation() {
+        setUpDisplaySizeWithApp(1000, 2500);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        assertTrue(mActivity.isEligibleForLetterboxEducation());
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertTrue(mActivity.inSizeCompatMode());
+    }
+
     /**
      * Tests that all three paths in which aspect ratio logic can be applied yield the same
      * result, which is that aspect ratio is respected on app bounds. The three paths are
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 80192f7..459e3a5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -105,7 +105,7 @@
  * Tests for the {@link WindowState} class.
  *
  * Build/Install/Run:
- *  atest WmTests:WindowStateTests
+ * atest WmTests:WindowStateTests
  */
 @SmallTest
 @Presubmit
@@ -411,7 +411,7 @@
         assertFalse(app.canAffectSystemUiFlags());
     }
 
-    @UseTestDisplay(addWindows = { W_ACTIVITY, W_STATUS_BAR })
+    @UseTestDisplay(addWindows = {W_ACTIVITY, W_STATUS_BAR})
     @Test
     public void testVisibleWithInsetsProvider() {
         final WindowState statusBar = mStatusBarWindow;
@@ -419,7 +419,8 @@
         statusBar.mHasSurface = true;
         assertTrue(statusBar.isVisible());
         mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
-                .setWindow(statusBar, null /* frameProvider */, null /* imeFrameProvider */);
+                .setWindowContainer(statusBar, null /* frameProvider */,
+                        null /* imeFrameProvider */);
         mDisplayContent.getInsetsStateController().onBarControlTargetChanged(
                 app, null /* fakeTopControlling */, app, null /* fakeNavControlling */);
         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
@@ -623,7 +624,7 @@
         assertEquals(w.getWindowConfiguration().getBounds(), unscaledClientBounds);
     }
 
-    @UseTestDisplay(addWindows = { W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE })
+    @UseTestDisplay(addWindows = {W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE})
     @Test
     public void testRequestDrawIfNeeded() {
         final WindowState startingApp = createWindow(null /* parent */,
@@ -831,7 +832,7 @@
         assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
     }
 
-    @UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
+    @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD})
     @Test
     public void testNeedsRelativeLayeringToIme_startingWindow() {
         WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING,
@@ -864,7 +865,7 @@
         verify(app).notifyInsetsChanged();
     }
 
-    @UseTestDisplay(addWindows = { W_INPUT_METHOD, W_ACTIVITY })
+    @UseTestDisplay(addWindows = {W_INPUT_METHOD, W_ACTIVITY})
     @Test
     public void testImeAlwaysReceivesVisibleNavigationBarInsets() {
         final InsetsSource navSource = new InsetsSource(ITYPE_NAVIGATION_BAR);
@@ -890,7 +891,7 @@
         mDisplayContent.mInputMethodWindow = imeWindow;
 
         final InsetsStateController controller = mDisplayContent.getInsetsStateController();
-        controller.getImeSourceProvider().setWindow(imeWindow, null, null);
+        controller.getImeSourceProvider().setWindowContainer(imeWindow, null, null);
 
         // Simulate app requests IME with updating all windows Insets State when IME is above app.
         mDisplayContent.setImeLayeringTarget(app);
@@ -914,7 +915,7 @@
         assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
     }
 
-    @UseTestDisplay(addWindows = { W_ACTIVITY })
+    @UseTestDisplay(addWindows = {W_ACTIVITY})
     @Test
     public void testUpdateImeControlTargetWhenLeavingMultiWindow() {
         WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
@@ -940,7 +941,7 @@
         assertEquals(mAppWindow, mDisplayContent.getImeTarget(IME_TARGET_CONTROL).getWindow());
     }
 
-    @UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE })
+    @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE})
     @Test
     public void testNotificationShadeHasImeInsetsWhenMultiWindow() {
         WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
@@ -954,7 +955,7 @@
         mNotificationShadeWindow.setHasSurface(true);
         mNotificationShadeWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
         assertTrue(mNotificationShadeWindow.canBeImeTarget());
-        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindow(
+        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindowContainer(
                 mImeWindow, null, null);
 
         mDisplayContent.computeImeTarget(true);
@@ -963,8 +964,7 @@
                 .setSourceVisible(ITYPE_IME, true);
 
         // Verify notificationShade can still get IME insets even windowing mode is multi-window.
-        InsetsState state = mDisplayContent.getInsetsStateController().getInsetsForWindow(
-                mNotificationShadeWindow);
+        InsetsState state = mNotificationShadeWindow.getInsetsState();
         assertNotNull(state.peekSource(ITYPE_IME));
         assertTrue(state.getSource(ITYPE_IME).isVisible());
     }
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 5dbb4c5..bd1f9d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -941,6 +941,7 @@
         private boolean mOnTop = false;
         private ActivityInfo.WindowLayout mWindowLayout;
         private boolean mVisible = true;
+        private ActivityOptions mLaunchIntoPipOpts;
 
         ActivityBuilder(ActivityTaskManagerService service) {
             mService = service;
@@ -1076,6 +1077,11 @@
             return this;
         }
 
+        ActivityBuilder setLaunchIntoPipActivityOptions(ActivityOptions opts) {
+            mLaunchIntoPipOpts = opts;
+            return this;
+        }
+
         ActivityRecord build() {
             SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);
             try {
@@ -1132,7 +1138,9 @@
             }
 
             ActivityOptions options = null;
-            if (mLaunchTaskBehind) {
+            if (mLaunchIntoPipOpts != null) {
+                options = mLaunchIntoPipOpts;
+            } else if (mLaunchTaskBehind) {
                 options = ActivityOptions.makeTaskLaunchBehind();
             }
             final ActivityRecord activity = new ActivityRecord.Builder(mService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 74cff10..4b5f330a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -262,7 +262,7 @@
     @Test
     public void testSetInsetsFrozen_notAffectImeWindowState() {
         // Pre-condition: make the IME window be controlled by IME insets provider.
-        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindow(
+        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindowContainer(
                 mDisplayContent.mInputMethodWindow, null, null);
 
         // Simulate an app window to be the IME layering target, assume the app window has no
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index fd9b995..42a5af7 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -36,7 +36,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -107,12 +106,6 @@
         return false;
     }
 
-    /**
-     * List of connected MIDI devices
-     */
-    private final HashMap<String, UsbMidiDevice>
-            mMidiDevices = new HashMap<String, UsbMidiDevice>();
-
     // UsbMidiDevice for USB peripheral mode (gadget) device
     private UsbMidiDevice mPeripheralMidiDevice = null;
 
@@ -256,45 +249,6 @@
             }
         }
 
-        // look for MIDI devices
-        boolean hasMidi = parser.hasMIDIInterface();
-        int midiNumInputs = parser.calculateNumLegacyMidiInputs();
-        int midiNumOutputs = parser.calculateNumLegacyMidiOutputs();
-        if (DEBUG) {
-            Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
-            Slog.d(TAG, "midiNumInputs: " + midiNumInputs + " midiNumOutputs:" + midiNumOutputs);
-        }
-        if (hasMidi && mHasMidiFeature) {
-            int device = 0;
-            Bundle properties = new Bundle();
-            String manufacturer = usbDevice.getManufacturerName();
-            String product = usbDevice.getProductName();
-            String version = usbDevice.getVersion();
-            String name;
-            if (manufacturer == null || manufacturer.isEmpty()) {
-                name = product;
-            } else if (product == null || product.isEmpty()) {
-                name = manufacturer;
-            } else {
-                name = manufacturer + " " + product;
-            }
-            properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
-            properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
-            properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
-            properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
-            properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
-                    usbDevice.getSerialNumber());
-            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum());
-            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/);
-            properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
-
-            UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
-                    cardRec.getCardNum(), 0 /*device*/, midiNumInputs, midiNumOutputs);
-            if (usbMidiDevice != null) {
-                mMidiDevices.put(deviceAddress, usbMidiDevice);
-            }
-        }
-
         logDevices("deviceAdded()");
 
         if (DEBUG) {
@@ -315,13 +269,6 @@
             selectDefaultDevice(); // if there any external devices left, select one of them
         }
 
-        // MIDI
-        UsbMidiDevice usbMidiDevice = mMidiDevices.remove(deviceAddress);
-        if (usbMidiDevice != null) {
-            Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
-            IoUtils.closeQuietly(usbMidiDevice);
-        }
-
         logDevices("usbDeviceRemoved()");
 
     }
@@ -377,12 +324,6 @@
             usbAlsaDevice.dump(dump, "alsa_devices", UsbAlsaManagerProto.ALSA_DEVICES);
         }
 
-        for (String deviceAddr : mMidiDevices.keySet()) {
-            // A UsbMidiDevice does not have a handle to the UsbDevice anymore
-            mMidiDevices.get(deviceAddr).dump(deviceAddr, dump, "midi_devices",
-                    UsbAlsaManagerProto.MIDI_DEVICES);
-        }
-
         dump.end(token);
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
similarity index 65%
rename from services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
rename to services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index 13d404c..0fa79df 100644
--- a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -20,6 +20,7 @@
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbDeviceConnection;
 import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceServer;
@@ -43,16 +44,20 @@
 import java.util.ArrayList;
 
 /**
- * A MIDI device that opens device connections to MIDI 2.0 endpoints.
+ * Opens device connections to MIDI 1.0 or MIDI 2.0 endpoints.
+ * This endpoint will not use ALSA and opens a UsbDeviceConnection directly.
  */
-public final class UsbUniversalMidiDevice implements Closeable {
-    private static final String TAG = "UsbUniversalMidiDevice";
+public final class UsbDirectMidiDevice implements Closeable {
+    private static final String TAG = "UsbDirectMidiDevice";
     private static final boolean DEBUG = false;
 
     private Context mContext;
     private UsbDevice mUsbDevice;
     private UsbDescriptorParser mParser;
     private ArrayList<UsbInterfaceDescriptor> mUsbInterfaces;
+    private final boolean mIsUniversalMidiDevice;
+    private final String mUniqueUsbDeviceIdentifier;
+    private final boolean mShouldCallSetInterface;
 
     // USB outputs are MIDI inputs
     private final InputReceiverProxy[] mMidiInputPortReceivers;
@@ -64,6 +69,11 @@
     // event schedulers for each input port of the physical device
     private MidiEventScheduler[] mEventSchedulers;
 
+    // Arbitrary number for timeout to not continue sending/receiving number from
+    // an inactive device. This number tries to balances the number of cycles and
+    // not being permanently stuck.
+    private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 100;
+
     private ArrayList<UsbDeviceConnection> mUsbDeviceConnections;
     private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints;
     private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints;
@@ -74,6 +84,8 @@
     private final Object mLock = new Object();
     private boolean mIsOpen;
 
+    private final UsbMidiPacketConverter mUsbMidiPacketConverter = new UsbMidiPacketConverter();
+
     private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
 
         @Override
@@ -140,13 +152,15 @@
     }
 
     /**
-     * Creates an UsbUniversalMidiDevice based on the input parameters. Read/Write streams
+     * Creates an UsbDirectMidiDevice based on the input parameters. Read/Write streams
      * will be created individually as some devices don't have the same number of
      * inputs and outputs.
      */
-    public static UsbUniversalMidiDevice create(Context context, UsbDevice usbDevice,
-            UsbDescriptorParser parser) {
-        UsbUniversalMidiDevice midiDevice = new UsbUniversalMidiDevice(usbDevice, parser);
+    public static UsbDirectMidiDevice create(Context context, UsbDevice usbDevice,
+            UsbDescriptorParser parser, boolean isUniversalMidiDevice,
+            String uniqueUsbDeviceIdentifier) {
+        UsbDirectMidiDevice midiDevice = new UsbDirectMidiDevice(usbDevice, parser,
+                isUniversalMidiDevice, uniqueUsbDeviceIdentifier);
         if (!midiDevice.register(context)) {
             IoUtils.closeQuietly(midiDevice);
             Log.e(TAG, "createDeviceServer failed");
@@ -155,11 +169,22 @@
         return midiDevice;
     }
 
-    private UsbUniversalMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser) {
+    private UsbDirectMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser,
+            boolean isUniversalMidiDevice, String uniqueUsbDeviceIdentifier) {
         mUsbDevice = usbDevice;
         mParser = parser;
+        mUniqueUsbDeviceIdentifier = uniqueUsbDeviceIdentifier;
+        mIsUniversalMidiDevice = isUniversalMidiDevice;
 
-        mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+        // Set interface should only be called when alternate interfaces exist.
+        // Otherwise, USB devices may not handle this gracefully.
+        mShouldCallSetInterface = (parser.calculateMidiInterfaceDescriptorsCount() > 1);
+
+        if (isUniversalMidiDevice) {
+            mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+        } else {
+            mUsbInterfaces = parser.findLegacyMidiInterfaceDescriptors();
+        }
 
         int numInputs = 0;
         int numOutputs = 0;
@@ -182,8 +207,8 @@
         mNumInputs = numInputs;
         mNumOutputs = numOutputs;
 
-        Log.d(TAG, "Created UsbUniversalMidiDevice with " + numInputs + " inputs and "
-                + numOutputs + " outputs");
+        Log.d(TAG, "Created UsbDirectMidiDevice with " + numInputs + " inputs and "
+                + numOutputs + " outputs. isUniversalMidiDevice: " + isUniversalMidiDevice);
 
         // Create MIDI port receivers based on the number of output ports. The
         // output of USB is the input of MIDI.
@@ -218,23 +243,25 @@
             if (doesInterfaceContainInput
                     && doesInterfaceContainOutput) {
                 UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
-
-                // The ALSA does not handle switching to the MIDI 2.0 interface correctly
-                // and stops exposing /dev/snd/midiC1D0 after calling connection.setInterface().
-                // Thus, simply use the control interface (interface zero).
+                UsbInterface usbInterface = interfaceDescriptor.toAndroid(mParser);
+                if (!updateUsbInterface(usbInterface, connection)) {
+                    continue;
+                }
                 int defaultMidiProtocol = mMidiBlockParser.calculateMidiType(connection,
-                        0,
+                        interfaceDescriptor.getInterfaceNumber(),
                         interfaceDescriptor.getAlternateSetting());
+
                 connection.close();
                 return defaultMidiProtocol;
             }
         }
 
-        Log.d(TAG, "Cannot find interface with both input and output endpoints");
+        Log.w(TAG, "Cannot find interface with both input and output endpoints");
         return MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
     }
 
     private boolean openLocked() {
+        Log.d(TAG, "openLocked()");
         UsbManager manager = mContext.getSystemService(UsbManager.class);
 
         mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(mUsbInterfaces.size());
@@ -258,8 +285,10 @@
             }
             if (!outputEndpoints.isEmpty() || !inputEndpoints.isEmpty()) {
                 UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
-                connection.setInterface(interfaceDescriptor.toAndroid(mParser));
-                connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true);
+                UsbInterface usbInterface = interfaceDescriptor.toAndroid(mParser);
+                if (!updateUsbInterface(usbInterface, connection)) {
+                    continue;
+                }
                 mUsbDeviceConnections.add(connection);
                 mInputUsbEndpoints.add(inputEndpoints);
                 mOutputUsbEndpoints.add(outputEndpoints);
@@ -283,14 +312,17 @@
             for (int endpointIndex = 0;
                     endpointIndex < mInputUsbEndpoints.get(connectionIndex).size();
                     endpointIndex++) {
-                final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
-                final UsbEndpoint epF = mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
-                final int portF = portNumber;
+                final UsbDeviceConnection connectionFinal =
+                        mUsbDeviceConnections.get(connectionIndex);
+                final UsbEndpoint endpointFinal =
+                        mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
+                final int portFinal = portNumber;
 
-                new Thread("UsbUniversalMidiDevice input thread " + portF) {
+                new Thread("UsbDirectMidiDevice input thread " + portFinal) {
                     @Override
                     public void run() {
-                        byte[] inputBuffer = new byte[epF.getMaxPacketSize()];
+                        byte[] inputBuffer = new byte[endpointFinal.getMaxPacketSize()];
+                        Log.d(TAG, "input buffer size: " + inputBuffer.length);
                         try {
                             while (true) {
                                 // Record time of event immediately after waking.
@@ -298,20 +330,34 @@
                                 synchronized (mLock) {
                                     if (!mIsOpen) break;
 
-                                    int nRead = connectionF.bulkTransfer(epF, inputBuffer,
-                                            inputBuffer.length, 0);
-
-                                    // For USB, each 32 bit word of a UMP is
-                                    // sent with the least significant byte first.
-                                    swapEndiannessPerWord(inputBuffer, inputBuffer.length);
+                                    int nRead = connectionFinal.bulkTransfer(endpointFinal,
+                                            inputBuffer, inputBuffer.length,
+                                            BULK_TRANSFER_TIMEOUT_MILLISECONDS);
 
                                     if (nRead > 0) {
                                         if (DEBUG) {
-                                            logByteArray("Input ", inputBuffer, 0,
-                                                    nRead);
+                                            logByteArray("Input before conversion ", inputBuffer,
+                                                    0, nRead);
                                         }
-                                        outputReceivers[portF].send(inputBuffer, 0, nRead,
-                                                timestamp);
+                                        byte[] convertedArray;
+                                        if (mIsUniversalMidiDevice) {
+                                            // For USB, each 32 bit word of a UMP is
+                                            // sent with the least significant byte first.
+                                            convertedArray = swapEndiannessPerWord(inputBuffer,
+                                                    nRead);
+                                        } else {
+                                            convertedArray =
+                                                    mUsbMidiPacketConverter.usbMidiToRawMidi(
+                                                             inputBuffer, nRead);
+                                        }
+
+                                        if (DEBUG) {
+                                            logByteArray("Input after conversion ", convertedArray,
+                                                    0, convertedArray.length);
+                                        }
+
+                                        outputReceivers[portFinal].send(convertedArray, 0,
+                                                convertedArray.length, timestamp);
                                     }
                                 }
                             }
@@ -333,19 +379,20 @@
             for (int endpointIndex = 0;
                     endpointIndex < mOutputUsbEndpoints.get(connectionIndex).size();
                     endpointIndex++) {
-                final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
-                final UsbEndpoint epF =
+                final UsbDeviceConnection connectionFinal =
+                        mUsbDeviceConnections.get(connectionIndex);
+                final UsbEndpoint endpointFinal =
                         mOutputUsbEndpoints.get(connectionIndex).get(endpointIndex);
-                final int portF = portNumber;
-                final MidiEventScheduler eventSchedulerF = mEventSchedulers[portF];
+                final int portFinal = portNumber;
+                final MidiEventScheduler eventSchedulerFinal = mEventSchedulers[portFinal];
 
-                new Thread("UsbUniversalMidiDevice output thread " + portF) {
+                new Thread("UsbDirectMidiDevice output thread " + portFinal) {
                     @Override
                     public void run() {
                         while (true) {
                             MidiEvent event;
                             try {
-                                event = (MidiEvent) eventSchedulerF.waitNextEvent();
+                                event = (MidiEvent) eventSchedulerFinal.waitNextEvent();
                             } catch (InterruptedException e) {
                                 // try again
                                 continue;
@@ -354,16 +401,32 @@
                                 break;
                             }
 
-                            // For USB, each 32 bit word of a UMP is
-                            // sent with the least significant byte first.
-                            swapEndiannessPerWord(event.data, event.count);
-
                             if (DEBUG) {
-                                logByteArray("Output ", event.data, 0,
+                                logByteArray("Output before conversion ", event.data, 0,
                                         event.count);
                             }
-                            connectionF.bulkTransfer(epF, event.data, event.count, 0);
-                            eventSchedulerF.addEventToPool(event);
+
+                            byte[] convertedArray;
+                            if (mIsUniversalMidiDevice) {
+                                // For USB, each 32 bit word of a UMP is
+                                // sent with the least significant byte first.
+                                convertedArray = swapEndiannessPerWord(event.data,
+                                        event.count);
+                            } else {
+                                convertedArray =
+                                        mUsbMidiPacketConverter.rawMidiToUsbMidi(
+                                                 event.data, event.count);
+                            }
+
+                            if (DEBUG) {
+                                logByteArray("Output after conversion ", convertedArray, 0,
+                                        convertedArray.length);
+                            }
+
+                            connectionFinal.bulkTransfer(endpointFinal, convertedArray,
+                                    convertedArray.length,
+                                    BULK_TRANSFER_TIMEOUT_MILLISECONDS);
+                            eventSchedulerFinal.addEventToPool(event);
                         }
                         Log.d(TAG, "output thread exit");
                     }
@@ -381,11 +444,15 @@
         mContext = context;
         MidiManager midiManager = context.getSystemService(MidiManager.class);
         if (midiManager == null) {
-            Log.e(TAG, "No MidiManager in UsbUniversalMidiDevice.create()");
+            Log.e(TAG, "No MidiManager in UsbDirectMidiDevice.create()");
             return false;
         }
 
-        mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+        if (mIsUniversalMidiDevice) {
+            mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+        } else {
+            mDefaultMidiProtocol = MidiDeviceInfo.PROTOCOL_UNKNOWN;
+        }
 
         Bundle properties = new Bundle();
         String manufacturer = mUsbDevice.getManufacturerName();
@@ -397,8 +464,15 @@
         } else if (product == null || product.isEmpty()) {
             name = manufacturer;
         } else {
-            name = manufacturer + " " + product + " MIDI 2.0";
+            name = manufacturer + " " + product;
         }
+        name += "#" + mUniqueUsbDeviceIdentifier;
+        if (mIsUniversalMidiDevice) {
+            name += " MIDI 2.0";
+        } else {
+            name += " MIDI 1.0";
+        }
+        Log.e(TAG, name);
         properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
         properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
         properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
@@ -430,10 +504,13 @@
     }
 
     private void closeLocked() {
+        Log.d(TAG, "closeLocked()");
         for (int i = 0; i < mEventSchedulers.length; i++) {
             mMidiInputPortReceivers[i].setReceiver(null);
             mEventSchedulers[i].close();
         }
+        mEventSchedulers = null;
+
         for (UsbDeviceConnection connection : mUsbDeviceConnections) {
             connection.close();
         }
@@ -444,15 +521,19 @@
         mIsOpen = false;
     }
 
-    private void swapEndiannessPerWord(byte[] array, int size) {
-        for (int i = 0; i + 3 < size; i += 4) {
-            byte tmp = array[i];
-            array[i] = array[i + 3];
-            array[i + 3] = tmp;
-            tmp = array[i + 1];
-            array[i + 1] = array[i + 2];
-            array[i + 2] = tmp;
+    private byte[] swapEndiannessPerWord(byte[] inputArray, int size) {
+        int numberOfExcessBytes = size & 3;
+        if (numberOfExcessBytes != 0) {
+            Log.e(TAG, "size not multiple of 4: " + size);
         }
+        byte[] outputArray = new byte[size - numberOfExcessBytes];
+        for (int i = 0; i + 3 < size; i += 4) {
+            outputArray[i] = inputArray[i + 3];
+            outputArray[i + 1] = inputArray[i + 2];
+            outputArray[i + 2] = inputArray[i + 1];
+            outputArray[i + 3] = inputArray[i];
+        }
+        return outputArray;
     }
 
     private static void logByteArray(String prefix, byte[] value, int offset, int count) {
@@ -465,4 +546,24 @@
         }
         Log.d(TAG, builder.toString());
     }
+
+    private boolean updateUsbInterface(UsbInterface usbInterface,
+            UsbDeviceConnection connection) {
+        if (usbInterface == null) {
+            Log.e(TAG, "Usb Interface is null");
+            return false;
+        }
+        if (!connection.claimInterface(usbInterface, true)) {
+            Log.e(TAG, "Can't claim interface");
+            return false;
+        }
+        if (mShouldCallSetInterface) {
+            if (!connection.setInterface(usbInterface)) {
+                Log.w(TAG, "Can't set interface");
+            }
+        } else {
+            Log.w(TAG, "no alternate interface");
+        }
+        return true;
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 94cc826..a70b0332 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -50,9 +50,12 @@
 import libcore.io.IoUtils;
 
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.Random;
 
 /**
  * UsbHostManager manages USB state in host mode.
@@ -94,10 +97,12 @@
     private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>();
 
     /**
-     * List of connected MIDI devices
+     * List of connected MIDI devices. Key on deviceAddress.
      */
-    private final HashMap<String, UsbUniversalMidiDevice>
-            mMidiDevices = new HashMap<String, UsbUniversalMidiDevice>();
+    private final HashMap<String, ArrayList<UsbDirectMidiDevice>>
+            mMidiDevices = new HashMap<String, ArrayList<UsbDirectMidiDevice>>();
+    private final HashSet<String> mMidiUniqueCodes = new HashSet<String>();
+    private final Random mRandom = new Random();
     private final boolean mHasMidiFeature;
 
     /*
@@ -423,15 +428,35 @@
                 mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
 
                 if (mHasMidiFeature) {
+                    // Use a 3 digit code to associate MIDI devices with one another.
+                    // Each MIDI device already has mId for uniqueness. mId is generated
+                    // sequentially. For clarity, this code is not generated sequentially.
+                    String uniqueUsbDeviceIdentifier = generateNewUsbDeviceIdentifier();
+
+                    ArrayList<UsbDirectMidiDevice> midiDevices =
+                            new ArrayList<UsbDirectMidiDevice>();
                     if (parser.containsUniversalMidiDeviceEndpoint()) {
-                        UsbUniversalMidiDevice midiDevice = UsbUniversalMidiDevice.create(mContext,
-                                newDevice, parser);
+                        UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
+                                newDevice, parser, true, uniqueUsbDeviceIdentifier);
                         if (midiDevice != null) {
-                            mMidiDevices.put(deviceAddress, midiDevice);
+                            midiDevices.add(midiDevice);
                         } else {
                             Slog.e(TAG, "Universal Midi Device is null.");
                         }
                     }
+                    if (parser.containsLegacyMidiDeviceEndpoint()) {
+                        UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
+                                newDevice, parser, false, uniqueUsbDeviceIdentifier);
+                        if (midiDevice != null) {
+                            midiDevices.add(midiDevice);
+                        } else {
+                            Slog.e(TAG, "Legacy Midi Device is null.");
+                        }
+                    }
+
+                    if (!midiDevices.isEmpty()) {
+                        mMidiDevices.put(deviceAddress, midiDevices);
+                    }
                 }
 
                 // Tracking
@@ -469,10 +494,13 @@
                 mPermissionManager.usbDeviceRemoved(device);
 
                 // MIDI
-                UsbUniversalMidiDevice midiDevice = mMidiDevices.remove(deviceAddress);
-                if (midiDevice != null) {
-                    Slog.i(TAG, "USB Universal MIDI Device Removed: " + deviceAddress);
-                    IoUtils.closeQuietly(midiDevice);
+                ArrayList<UsbDirectMidiDevice> midiDevices =
+                        mMidiDevices.remove(deviceAddress);
+                for (UsbDirectMidiDevice midiDevice : midiDevices) {
+                    if (midiDevice != null) {
+                        Slog.i(TAG, "USB MIDI Device Removed: " + deviceAddress);
+                        IoUtils.closeQuietly(midiDevice);
+                    }
                 }
 
                 getCurrentUserSettings().usbDeviceRemoved(device);
@@ -606,6 +634,19 @@
         return true;
     }
 
+    // Generate a 3 digit code.
+    private String generateNewUsbDeviceIdentifier() {
+        String code;
+        do {
+            code = "";
+            for (int i = 0; i < 3; i++) {
+                code += mRandom.nextInt(10);
+            }
+        } while (mMidiUniqueCodes.contains(code));
+        mMidiUniqueCodes.add(code);
+        return code;
+    }
+
     private native void monitorUsbHostBus();
     private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
 }
diff --git a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
new file mode 100644
index 0000000..7c93c76
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.usb;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Converts between MIDI packets and USB MIDI 1.0 packets.
+ */
+public class UsbMidiPacketConverter {
+
+    // Refer to Table 4-1 in USB MIDI 1.0 spec.
+    private static final int[] PAYLOAD_SIZE = new int[]{
+            /* 0x00 */ -1, // Miscellaneous function codes. Reserved for future extensions.
+            /* 0x01 */ -1, // Cable events. Reserved for future expansion.
+            /* 0x02 */  2, // Two-byte System Common messages like MTC, SongSelect, etc
+            /* 0x03 */  3, // Three-byte System Common messages like SPP, etc.
+            /* 0x04 */  3, // SysEx starts or continues
+            /* 0x05 */  1, // Single-byte System Common Message or single-byte SysEx ends.
+            /* 0x06 */  2, // SysEx ends with following two bytes.
+            /* 0x07 */  3, // SysEx ends with following three bytes.
+            /* 0x08 */  3, // Note-off
+            /* 0x09 */  3, // Note-on
+            /* 0x0a */  3, // Poly-KeyPress
+            /* 0x0b */  3, // Control Change
+            /* 0x0c */  2, // Program Change
+            /* 0x0d */  2, // Channel Pressure
+            /* 0x0e */  3, // PitchBend Change
+            /* 0x0f */  1  // Single Byte
+    };
+
+    // Each System MIDI message is a certain size. These can be mapped to a
+    // Code Index number defined in Table 4-1 of USB MIDI 1.0.
+    private static final int[] CODE_INDEX_NUMBER_FROM_SYSTEM_TYPE = new int[]{
+            /* 0x00 */ -1, // Start of Exclusive. Special case.
+            /* 0x01 */  2, // MIDI Time Code. Two byte message
+            /* 0x02 */  3, // Song Point Pointer. Three byte message
+            /* 0x03 */  2, // Song Select. Two byte message
+            /* 0x04 */ -1, // Undefined MIDI System Common
+            /* 0x05 */ -1, // Undefined MIDI System Common
+            /* 0x06 */  5, // Tune Request. One byte message
+            /* 0x07 */ -1, // End of Exclusive. Special case.
+            /* 0x08 */  5, // Timing clock. One byte message
+            /* 0x09 */ -1, // Undefined MIDI System Real-time
+            /* 0x0a */  5, // Start. One byte message
+            /* 0x0b */  5, // Continue. One byte message
+            /* 0x0c */  5, // Stop. One byte message
+            /* 0x0d */ -1, // Undefined MIDI System Real-time
+            /* 0x0e */  5, // Active Sensing. One byte message
+            /* 0x0f */  5  // System Reset. One byte message
+    };
+
+    // These code index numbers also come from Table 4-1 in USB MIDI 1.0 spec.
+    private static final byte CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES = 0x4;
+    private static final byte CODE_INDEX_NUMBER_SINGLE_BYTE = 0xF;
+    private static final byte CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE = (byte) 0x5;
+
+    // System messages are defined in MIDI.
+    private static final byte FIRST_SYSTEM_MESSAGE_VALUE = (byte) 0xF0;
+    private static final byte SYSEX_START_EXCLUSIVE = (byte) 0xF0;
+    private static final byte SYSEX_END_EXCLUSIVE = (byte) 0xF7;
+
+    private UsbMidiEncoder mUsbMidiEncoder = new UsbMidiEncoder();
+    private UsbMidiDecoder mUsbMidiDecoder = new UsbMidiDecoder();
+
+    /**
+     * Converts a USB MIDI array into a raw MIDI array.
+     *
+     * @param usbMidiBytes the USB MIDI bytes to convert
+     * @param size the size of usbMidiBytes
+     * @return byte array of raw MIDI packets
+     */
+    public byte[] usbMidiToRawMidi(byte[] usbMidiBytes, int size) {
+        return mUsbMidiDecoder.decode(usbMidiBytes, size);
+    }
+
+    /**
+     * Converts a raw MIDI array into a USB MIDI array.
+     *
+     * @param midiBytes the raw MIDI bytes to convert
+     * @param size the size of usbMidiBytes
+     * @return byte array of USB MIDI packets
+     */
+    public byte[] rawMidiToUsbMidi(byte[] midiBytes, int size) {
+        return mUsbMidiEncoder.encode(midiBytes, size);
+    }
+
+    private class UsbMidiDecoder {
+        // Decodes the data from USB MIDI to raw MIDI.
+        // Each valid 4 byte input maps to a 1-3 byte output.
+        // Reference the USB MIDI 1.0 spec for more info.
+        public byte[] decode(byte[] usbMidiBytes, int size) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            for (int i = 0; i + 3 < size; i += 4) {
+                int codeIndex = usbMidiBytes[i] & 0x0f;
+                int numPayloadBytes = PAYLOAD_SIZE[codeIndex];
+                if (numPayloadBytes < 0) {
+                    continue;
+                }
+                outputStream.write(usbMidiBytes, i + 1, numPayloadBytes);
+            }
+            return outputStream.toByteArray();
+        }
+    }
+
+    private class UsbMidiEncoder {
+        // In order to facilitate large scale transfers, SysEx can be sent in multiple packets.
+        // If encode() is called without an SysEx end, we must continue SysEx for the next packet.
+        // All other packets should be 3 bytes or less and must be not be broken between packets.
+        private byte[] mStoredSystemExclusiveBytes = new byte[3];
+        private int mNumStoredSystemExclusiveBytes = 0;
+        private boolean mHasSystemExclusiveStarted = false;
+
+        private byte[] mEmptyBytes = new byte[3]; // Used to fill out extra data
+
+        // Encodes the data from raw MIDI to USB MIDI.
+        // Each valid 1-3 byte input maps to a 4 byte output.
+        // Reference the USB MIDI 1.0 spec for more info.
+        // MidiFramer is not needed here as this code handles partial packets.
+        // Long SysEx messages split between packets will encode and return a
+        // byte stream even if the SysEx end has not been sent.
+        // If there are less than 3 remaining data bytes in a SysEx message left,
+        // these bytes will be combined with the next set of packets.
+        public byte[] encode(byte[] midiBytes, int size) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            int curLocation = 0;
+            while (curLocation < size) {
+                if (midiBytes[curLocation] >= 0) { // Data byte
+                    if (mHasSystemExclusiveStarted) {
+                        mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
+                                midiBytes[curLocation];
+                        mNumStoredSystemExclusiveBytes++;
+                        if (mNumStoredSystemExclusiveBytes == 3) {
+                            outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES);
+                            outputStream.write(mStoredSystemExclusiveBytes, 0, 3);
+                            mNumStoredSystemExclusiveBytes = 0;
+                        }
+                    } else {
+                        writeSingleByte(outputStream, midiBytes[curLocation]);
+                    }
+                    curLocation++;
+                    continue;
+                } else if (midiBytes[curLocation] != SYSEX_END_EXCLUSIVE) {
+                    // SysEx operation was interrupted. Pass the data directly down.
+                    if (mHasSystemExclusiveStarted) {
+                        int index = 0;
+                        while (index < mNumStoredSystemExclusiveBytes) {
+                            writeSingleByte(outputStream, mStoredSystemExclusiveBytes[index]);
+                            index++;
+                        }
+                        mNumStoredSystemExclusiveBytes = 0;
+                        mHasSystemExclusiveStarted = false;
+                    }
+                }
+
+                if (midiBytes[curLocation] < FIRST_SYSTEM_MESSAGE_VALUE) { // Channel message
+                    byte codeIndexNumber = (byte) ((midiBytes[curLocation] >> 4) & 0x0f);
+                    int channelMessageSize = PAYLOAD_SIZE[codeIndexNumber];
+                    if (curLocation + channelMessageSize <= size) {
+                        outputStream.write(codeIndexNumber);
+                        outputStream.write(midiBytes, curLocation, channelMessageSize);
+                        // Fill in the rest of the bytes with 0.
+                        outputStream.write(mEmptyBytes, 0, 3 - channelMessageSize);
+                        curLocation += channelMessageSize;
+                    } else { // The packet is missing data. Use single byte messages.
+                        while (curLocation < size) {
+                            writeSingleByte(outputStream, midiBytes[curLocation]);
+                            curLocation++;
+                        }
+                    }
+                } else if (midiBytes[curLocation] == SYSEX_START_EXCLUSIVE) {
+                    mHasSystemExclusiveStarted = true;
+                    mStoredSystemExclusiveBytes[0] = midiBytes[curLocation];
+                    mNumStoredSystemExclusiveBytes = 1;
+                    curLocation++;
+                } else if (midiBytes[curLocation] == SYSEX_END_EXCLUSIVE) {
+                    // 1 byte is 0x05, 2 bytes is 0x06, and 3 bytes is 0x07
+                    outputStream.write(CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE
+                            + mNumStoredSystemExclusiveBytes);
+                    mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
+                            midiBytes[curLocation];
+                    mNumStoredSystemExclusiveBytes++;
+                    outputStream.write(mStoredSystemExclusiveBytes, 0,
+                             mNumStoredSystemExclusiveBytes);
+                    // Fill in the rest of the bytes with 0.
+                    outputStream.write(mEmptyBytes, 0, 3 - mNumStoredSystemExclusiveBytes);
+                    mHasSystemExclusiveStarted = false;
+                    mNumStoredSystemExclusiveBytes = 0;
+                    curLocation++;
+                } else {
+                    int systemType = midiBytes[curLocation] & 0x0f;
+                    int codeIndexNumber = CODE_INDEX_NUMBER_FROM_SYSTEM_TYPE[systemType];
+                    if (codeIndexNumber < 0) { // Unknown type. Use single byte messages.
+                        writeSingleByte(outputStream, midiBytes[curLocation]);
+                        curLocation++;
+                    } else {
+                        int systemMessageSize = PAYLOAD_SIZE[codeIndexNumber];
+                        if (curLocation + systemMessageSize <= size) {
+                            outputStream.write(codeIndexNumber);
+                            outputStream.write(midiBytes, curLocation, systemMessageSize);
+                            // Fill in the rest of the bytes with 0.
+                            outputStream.write(mEmptyBytes, 0, 3 - systemMessageSize);
+                            curLocation += systemMessageSize;
+                        } else { // The packet is missing data. Use single byte messages.
+                            while (curLocation < size) {
+                                writeSingleByte(outputStream, midiBytes[curLocation]);
+                                curLocation++;
+                            }
+                        }
+                    }
+                }
+            }
+            return outputStream.toByteArray();
+        }
+
+        private void writeSingleByte(ByteArrayOutputStream outputStream, byte byteToWrite) {
+            outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE);
+            outputStream.write(byteToWrite);
+            outputStream.write(0);
+            outputStream.write(0);
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 6e68a91..cd6ea68 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -198,14 +198,20 @@
                 if (mCurInterfaceDescriptor != null) {
                     switch (mCurInterfaceDescriptor.getUsbClass()) {
                         case UsbDescriptor.CLASSID_AUDIO:
-                            descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+                            descriptor =
+                                    UsbACInterface.allocDescriptor(this, stream, length, type);
+                            if (descriptor instanceof UsbMSMidiHeader) {
+                                mCurInterfaceDescriptor.setMidiHeaderInterfaceDescriptor(
+                                        descriptor);
+                            }
                             break;
 
                         case UsbDescriptor.CLASSID_VIDEO:
                             if (DEBUG) {
                                 Log.d(TAG, "  UsbDescriptor.CLASSID_VIDEO");
                             }
-                            descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
+                            descriptor =
+                                    UsbVCInterface.allocDescriptor(this, stream, length, type);
                             break;
 
                         case UsbDescriptor.CLASSID_AUDIOVIDEO:
@@ -218,7 +224,6 @@
                             Log.w(TAG, "  Unparsed Class-specific");
                             break;
                     }
-                    mCurInterfaceDescriptor.setClassSpecificInterfaceDescriptor(descriptor);
                 }
                 break;
 
@@ -674,6 +679,23 @@
     public boolean containsUniversalMidiDeviceEndpoint() {
         ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
                 findUniversalMidiInterfaceDescriptors();
+        return doesInterfaceContainEndpoint(interfaceDescriptors);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean containsLegacyMidiDeviceEndpoint() {
+        ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
+                findLegacyMidiInterfaceDescriptors();
+        return doesInterfaceContainEndpoint(interfaceDescriptors);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean doesInterfaceContainEndpoint(
+            ArrayList<UsbInterfaceDescriptor> interfaceDescriptors) {
         int outputCount = 0;
         int inputCount = 0;
         for (int interfaceIndex = 0; interfaceIndex < interfaceDescriptors.size();
@@ -698,10 +720,24 @@
      * @hide
      */
     public ArrayList<UsbInterfaceDescriptor> findUniversalMidiInterfaceDescriptors() {
+        return findMidiInterfaceDescriptors(MS_MIDI_2_0);
+    }
+
+    /**
+     * @hide
+     */
+    public ArrayList<UsbInterfaceDescriptor> findLegacyMidiInterfaceDescriptors() {
+        return findMidiInterfaceDescriptors(MS_MIDI_1_0);
+    }
+
+    /**
+     * @hide
+     */
+    private ArrayList<UsbInterfaceDescriptor> findMidiInterfaceDescriptors(int type) {
         int count = 0;
         ArrayList<UsbDescriptor> descriptors =
                 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
-        ArrayList<UsbInterfaceDescriptor> universalMidiInterfaces =
+        ArrayList<UsbInterfaceDescriptor> midiInterfaces =
                 new ArrayList<UsbInterfaceDescriptor>();
 
         for (UsbDescriptor descriptor : descriptors) {
@@ -709,14 +745,14 @@
             if (descriptor instanceof UsbInterfaceDescriptor) {
                 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
                 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
-                    UsbDescriptor classSpecificDescriptor =
-                            interfaceDescriptor.getClassSpecificInterfaceDescriptor();
-                    if (classSpecificDescriptor != null) {
-                        if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+                    UsbDescriptor midiHeaderDescriptor =
+                            interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+                    if (midiHeaderDescriptor != null) {
+                        if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
                             UsbMSMidiHeader midiHeader =
-                                    (UsbMSMidiHeader) classSpecificDescriptor;
-                            if (midiHeader.getMidiStreamingClass() == MS_MIDI_2_0) {
-                                universalMidiInterfaces.add(interfaceDescriptor);
+                                    (UsbMSMidiHeader) midiHeaderDescriptor;
+                            if (midiHeader.getMidiStreamingClass() == type) {
+                                midiInterfaces.add(interfaceDescriptor);
                             }
                         }
                     }
@@ -726,10 +762,13 @@
                         + " t:0x" + Integer.toHexString(descriptor.getType()));
             }
         }
-        return universalMidiInterfaces;
+        return midiInterfaces;
     }
 
-    private int calculateNumLegacyMidiPorts(boolean isOutput) {
+    /**
+     * @hide
+     */
+    public int calculateMidiInterfaceDescriptorsCount() {
         int count = 0;
         ArrayList<UsbDescriptor> descriptors =
                 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
@@ -738,22 +777,12 @@
             if (descriptor instanceof UsbInterfaceDescriptor) {
                 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
                 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
-                    UsbDescriptor classSpecificDescriptor =
-                            interfaceDescriptor.getClassSpecificInterfaceDescriptor();
-                    if (classSpecificDescriptor != null) {
-                        if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+                    UsbDescriptor midiHeaderDescriptor =
+                            interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+                    if (midiHeaderDescriptor != null) {
+                        if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
                             UsbMSMidiHeader midiHeader =
-                                    (UsbMSMidiHeader) classSpecificDescriptor;
-                            if (midiHeader.getMidiStreamingClass() != MS_MIDI_1_0) {
-                                continue;
-                            }
-                        }
-                    }
-                    for (int i = 0; i < interfaceDescriptor.getNumEndpoints(); i++) {
-                        UsbEndpointDescriptor endpoint =
-                                interfaceDescriptor.getEndpointDescriptor(i);
-                        // 0 is output, 1 << 7 is input.
-                        if ((endpoint.getDirection() == 0) == isOutput) {
+                                    (UsbMSMidiHeader) midiHeaderDescriptor;
                             count++;
                         }
                     }
@@ -769,20 +798,6 @@
     /**
      * @hide
      */
-    public int calculateNumLegacyMidiInputs() {
-        return calculateNumLegacyMidiPorts(false /*isOutput*/);
-    }
-
-    /**
-     * @hide
-     */
-    public int calculateNumLegacyMidiOutputs() {
-        return calculateNumLegacyMidiPorts(true /*isOutput*/);
-    }
-
-    /**
-     * @hide
-     */
     public float getInputHeadsetProbability() {
         if (hasMIDIInterface()) {
             return 0.0f;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index ca4613b..9ddcb10 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -42,7 +42,8 @@
     private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
             new ArrayList<UsbEndpointDescriptor>();
 
-    private UsbDescriptor mClassSpecificInterfaceDescriptor;
+    // Used for MIDI only.
+    private UsbDescriptor mMidiHeaderInterfaceDescriptor;
 
     UsbInterfaceDescriptor(int length, byte type) {
         super(length, type);
@@ -107,12 +108,12 @@
         mEndpointDescriptors.add(endpoint);
     }
 
-    public void setClassSpecificInterfaceDescriptor(UsbDescriptor descriptor) {
-        mClassSpecificInterfaceDescriptor = descriptor;
+    public void setMidiHeaderInterfaceDescriptor(UsbDescriptor descriptor) {
+        mMidiHeaderInterfaceDescriptor = descriptor;
     }
 
-    public UsbDescriptor getClassSpecificInterfaceDescriptor() {
-        return mClassSpecificInterfaceDescriptor;
+    public UsbDescriptor getMidiHeaderInterfaceDescriptor() {
+        return mMidiHeaderInterfaceDescriptor;
     }
 
     /**
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index 98d13e8..566c725 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -26,16 +26,8 @@
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6600s" />
         <option name="hidden-api-checks" value="false" />
-        <option name="device-listeners"
-                value="com.android.server.wm.flicker.TraceFileReadyListener" />
     </test>
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
-        <option name="pull-pattern-keys" value="(\w)+\.winscope" />
-        <option name="pull-pattern-keys" value="(\w)+\.mp4" />
-        <option name="collect-on-run-ended-only" value="false" />
-        <option name="clean-up" value="true" />
-    </metrics_collector>
-    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="directory-keys" value="/sdcard/flicker" />
         <option name="collect-on-run-ended-only" value="true" />
         <option name="clean-up" value="true" />
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 3b3a303..f834820 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest_ShellTransit.kt
index 849e3ae..0d2869c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest_ShellTransit.kt
@@ -16,7 +16,7 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 18d017d..00fee82 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,10 +16,10 @@
 
 package com.android.server.wm.flicker.launch
 
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.platform.test.annotations.RequiresDevice
 import android.view.Display
-import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
@@ -31,7 +31,6 @@
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
 import org.junit.Assume.assumeFalse
-import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -140,37 +139,6 @@
     @Test
     override fun appWindowBecomesVisible() = super.appWindowBecomesVisible_warmStart()
 
-    /** {@inheritDoc} */
-    @Presubmit
-    @Test
-    override fun appWindowReplacesLauncherAsTopWindow() {
-        assumeFalse(isShellTransitionsEnabled)
-        super.appWindowReplacesLauncherAsTopWindow()
-    }
-
-    @FlakyTest(bugId = 216266712)
-    @Test
-    fun appWindowReplacesLauncherAsTopWindow_shellTransit() {
-        assumeTrue(isShellTransitionsEnabled)
-        super.appWindowReplacesLauncherAsTopWindow()
-    }
-
-    /** {@inheritDoc} */
-    @Presubmit
-    @Test
-    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
-        assumeFalse(isShellTransitionsEnabled)
-        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-    }
-
-    /** {@inheritDoc} */
-    @FlakyTest(bugId = 218470989)
-    @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry_shellTransit() {
-        assumeTrue(isShellTransitionsEnabled)
-        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-    }
-
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest_ShellTransit.kt
index 24716ff..1c06495 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest_ShellTransit.kt
@@ -16,7 +16,7 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -25,6 +25,7 @@
 import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -60,4 +61,16 @@
     override fun before() {
         assumeTrue(isShellTransitionsEnabled)
     }
+
+    /** {@inheritDoc} */
+    @FlakyTest(bugId = 216266712)
+    @Test
+    override fun appWindowReplacesLauncherAsTopWindow() =
+            super.appWindowReplacesLauncherAsTopWindow()
+
+    /** {@inheritDoc} */
+    @FlakyTest(bugId = 218470989)
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 0ba5369..b0e53e9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.platform.test.annotations.RequiresDevice
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest_ShellTransit.kt
index f8afcd9..8a08d07 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest_ShellTransit.kt
@@ -16,7 +16,7 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index d2f6c7f1..53560cc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.launch
 
 import android.app.Instrumentation
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Presubmit
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest_ShellTransit.kt
index 0ce73f4..3958dd2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest_ShellTransit.kt
@@ -16,7 +16,7 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
index 07fe274..cffed81 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
@@ -16,7 +16,7 @@
 
 package com.android.server.wm.flicker.quickswitch
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index f603f6e..2b944c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -112,10 +112,7 @@
      * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
      * doesn't flicker, and disappears before the transition is complete
      */
-    @Presubmit
-    @Test
-    fun rotationLayerAppearsAndVanishes() {
-        Assume.assumeFalse(isShellTransitionsEnabled)
+    fun rotationLayerAppearsAndVanishesAssertion() {
         testSpec.assertLayers {
             this.isVisible(testApp.component)
                 .then()
@@ -126,11 +123,26 @@
         }
     }
 
+    /**
+     * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
+     * doesn't flicker, and disappears before the transition is complete
+     */
+    @Presubmit
+    @Test
+    fun rotationLayerAppearsAndVanishes() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+        rotationLayerAppearsAndVanishesAssertion()
+    }
+
+    /**
+     * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
+     * doesn't flicker, and disappears before the transition is complete
+     */
     @FlakyTest(bugId = 218484127)
     @Test
     fun rotationLayerAppearsAndVanishes_shellTransit() {
         Assume.assumeTrue(isShellTransitionsEnabled)
-        rotationLayerAppearsAndVanishes()
+        rotationLayerAppearsAndVanishesAssertion()
     }
 
     /**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index d1bdeed..0becadf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -153,4 +153,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest_ShellTransit.kt
index be55751..d397d59 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest_ShellTransit.kt
@@ -16,7 +16,7 @@
 
 package com.android.server.wm.flicker.rotation
 
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index c59a41e..9a9e42b 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -37,8 +37,6 @@
         "vts",
     ],
     data: [
-        ":NotoSerif-Regular.ttf",
-        ":NotoSerif-Bold.ttf",
         ":UpdatableSystemFontTestCertDer",
         ":UpdatableSystemFontTest_NotoColorEmoji.ttf",
         ":UpdatableSystemFontTest_NotoColorEmoji.sig",
@@ -48,7 +46,9 @@
         ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
         ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
         ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
+        ":UpdatableSystemFontTest_NotoSerif-Regular.ttf",
         ":UpdatableSystemFontTest_NotoSerif-Regular.sig",
+        ":UpdatableSystemFontTest_NotoSerif-Bold.ttf",
         ":UpdatableSystemFontTest_NotoSerif-Bold.sig",
     ],
     sdk_version: "test_current",
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index 6effa7b..9e2a4b6 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -28,11 +28,7 @@
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
         <option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
-        <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
-        <option name="push" value="NotoSerif-Regular.ttf->/data/local/tmp/NotoSerif-Regular.ttf" />
-        <option name="push" value="NotoSerif-Bold.ttf->/data/local/tmp/NotoSerif-Bold.ttf" />
-        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
-        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.ttf" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig" />
@@ -40,6 +36,10 @@
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.ttf" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.ttf" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 87fda0d..cbe13d9 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -84,7 +84,7 @@
 
     private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
     private static final String NOTO_COLOR_EMOJI_TTF =
-            "/data/local/tmp/NotoColorEmoji.ttf";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.ttf";
     private static final String NOTO_COLOR_EMOJI_SIG =
             "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig";
     // A font with revision == 0.
@@ -105,13 +105,13 @@
 
     private static final String NOTO_SERIF_REGULAR_POSTSCRIPT_NAME = "NotoSerif";
     private static final String NOTO_SERIF_REGULAR_TTF =
-            "/data/local/tmp/NotoSerif-Regular.ttf";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.ttf";
     private static final String NOTO_SERIF_REGULAR_SIG =
             "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig";
 
     private static final String NOTO_SERIF_BOLD_POSTSCRIPT_NAME = "NotoSerif-Bold";
     private static final String NOTO_SERIF_BOLD_TTF =
-            "/data/local/tmp/NotoSerif-Bold.ttf";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.ttf";
     private static final String NOTO_SERIF_BOLD_SIG =
             "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig";
 
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 64b698d..0bdb3a8 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -21,11 +21,19 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-// An existing module name is reused to avoid merge conflicts.
-// TODO: fix the font file name.
 filegroup {
     name: "UpdatableSystemFontTest_NotoColorEmoji.ttf",
-    srcs: ["NotoColorEmoji.ttf"],
+    srcs: ["UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+}
+
+filegroup {
+    name: "UpdatableSystemFontTest_NotoSerif-Regular.ttf",
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Regular.ttf"],
+}
+
+filegroup {
+    name: "UpdatableSystemFontTest_NotoSerif-Bold.ttf",
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Bold.ttf"],
 }
 
 filegroup {
@@ -43,10 +51,6 @@
     srcs: ["UpdatableSystemFontTestCert.der"],
 }
 
-genrule_defaults {
-    name: "updatable_system_font_increment_font_revision_default",
-}
-
 genrule {
     name: "UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
     srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
@@ -124,13 +128,13 @@
 genrule {
     name: "UpdatableSystemFontTest_NotoSerif-Regular.sig",
     defaults: ["updatable_system_font_sig_gen_default"],
-    srcs: [":NotoSerif-Regular.ttf"],
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Regular.ttf"],
     out: ["UpdatableSystemFontTest_NotoSerif-Regular.sig"],
 }
 
 genrule {
     name: "UpdatableSystemFontTest_NotoSerif-Bold.sig",
     defaults: ["updatable_system_font_sig_gen_default"],
-    srcs: [":NotoSerif-Bold.ttf"],
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Bold.ttf"],
     out: ["UpdatableSystemFontTest_NotoSerif-Bold.sig"],
 }
diff --git a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf
similarity index 100%
rename from tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf
rename to tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx
similarity index 100%
rename from tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx
rename to tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf
new file mode 100644
index 0000000..66c1bd2
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx
new file mode 100644
index 0000000..8c4215e
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <!-- Currently NotoSerif-Bold.ttf's fontRevision is 1.xx.
+         100.0 will be sufficiently larger than that. -->
+    <fontRevision value="100.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Feb 16 12:00:00 2022"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="a" width="3000" lsb="93"/>  <!-- 3em -->
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <!-- length will be calculated by the compiler. -->
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="0" language="0" nGroups="1">
+      <!-- The font must support at least one of the characters used
+           in OtfFontFileParser to validate the font. -->
+      <map code="0x61" name="a" />
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="a" xMin="0" yMin="0" xMax="300" yMax="300">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="0" y="300" on="1" />
+        <pt x="300" y="300" on="1" />
+        <pt x="300" y="0" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2022 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      <!-- Android identifies the target font to be updated by PostScript name.
+           To test updating NotoSerif-Bold.ttf, the PostScript needs to be
+           the same as NotoSerif-Bold.ttf here. -->
+      NotoSerif-Bold
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf
new file mode 100644
index 0000000..707ae28
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx
new file mode 100644
index 0000000..754eae3
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <!-- Currently NotoSerif-Regular.ttf's fontRevision is 1.xx.
+         100.0 will be sufficiently larger than that. -->
+    <fontRevision value="100.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Feb 16 12:00:00 2022"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="a" width="3000" lsb="93"/>  <!-- 3em -->
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <!-- length will be calculated by the compiler. -->
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="0" language="0" nGroups="1">
+      <!-- The font must support at least one of the characters used
+           in OtfFontFileParser to validate the font. -->
+      <map code="0x61" name="a" />
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="a" xMin="0" yMin="0" xMax="300" yMax="300">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="0" y="300" on="1" />
+        <pt x="300" y="300" on="1" />
+        <pt x="300" y="0" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2022 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      <!-- Android identifies the target font to be updated by PostScript name.
+           To test updating NotoSerif-Regular.ttf, the PostScript needs to be
+           the same as NotoSerif-Regular.ttf here. -->
+      NotoSerif
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index e547400..4cfa93b 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -307,7 +307,10 @@
                         ncCaptor.capture(),
                         lpCaptor.capture(),
                         any(),
-                        argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE),
+                        // Subtype integer/name and extras do not have getters; cannot be tested.
+                        argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE
+                                && nac.getLegacyTypeName().equals(
+                                        VcnGatewayConnection.NETWORK_INFO_NETWORK_TYPE_STRING)),
                         any(),
                         any(),
                         any());