Merge "settings command list by user"
diff --git a/Android.bp b/Android.bp
index 1012bb81..7e038ce8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -254,6 +254,7 @@
         ":statsd_aidl",
         "core/java/android/os/ISystemUpdateManager.aidl",
         "core/java/android/os/IThermalEventListener.aidl",
+        "core/java/android/os/IThermalStatusListener.aidl",
         "core/java/android/os/IThermalService.aidl",
         "core/java/android/os/IUpdateLock.aidl",
         "core/java/android/os/IUserManager.aidl",
@@ -334,8 +335,10 @@
         "core/java/android/service/chooser/IChooserTargetResult.aidl",
         "core/java/android/service/resolver/IResolverRankerService.aidl",
         "core/java/android/service/resolver/IResolverRankerResult.aidl",
+        "core/java/android/service/textclassifier/IConversationActionsCallback.aidl",
         "core/java/android/service/textclassifier/ITextClassificationCallback.aidl",
         "core/java/android/service/textclassifier/ITextClassifierService.aidl",
+        "core/java/android/service/textclassifier/ITextLanguageCallback.aidl",
         "core/java/android/service/textclassifier/ITextLinksCallback.aidl",
         "core/java/android/service/textclassifier/ITextSelectionCallback.aidl",
         "core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl",
@@ -455,7 +458,6 @@
         "media/java/android/media/IMediaScannerListener.aidl",
         "media/java/android/media/IMediaScannerService.aidl",
         "media/java/android/media/IPlaybackConfigDispatcher.aidl",
-        "media/java/android/media/ISessionTokensListener.aidl",
         ":libaudioclient_aidl",
         "media/java/android/media/IRecordingConfigDispatcher.aidl",
         "media/java/android/media/IRemoteDisplayCallback.aidl",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ec3366c..5936ee4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -10,3 +10,5 @@
 ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
 
 owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
+
+shell_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^packages/Shell/"
diff --git a/api/current.txt b/api/current.txt
index aa400cd..e84bc8d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9601,6 +9601,7 @@
     method public abstract void unbindService(android.content.ServiceConnection);
     method public void unregisterComponentCallbacks(android.content.ComponentCallbacks);
     method public abstract void unregisterReceiver(android.content.BroadcastReceiver);
+    method public abstract void updateServiceGroup(android.content.ServiceConnection, int, int);
     field public static final java.lang.String ACCESSIBILITY_SERVICE = "accessibility";
     field public static final java.lang.String ACCOUNT_SERVICE = "account";
     field public static final java.lang.String ACTIVITY_SERVICE = "activity";
@@ -9799,6 +9800,7 @@
     method public boolean stopService(android.content.Intent);
     method public void unbindService(android.content.ServiceConnection);
     method public void unregisterReceiver(android.content.BroadcastReceiver);
+    method public void updateServiceGroup(android.content.ServiceConnection, int, int);
   }
 
   public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
@@ -42859,6 +42861,7 @@
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
+    field public static final java.lang.String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
     field public static final java.lang.String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
@@ -56237,6 +56240,7 @@
   }
 
   public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
+    ctor public InMemoryDexClassLoader(java.nio.ByteBuffer[], java.lang.String, java.lang.ClassLoader);
     ctor public InMemoryDexClassLoader(java.nio.ByteBuffer[], java.lang.ClassLoader);
     ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 8d800bc..bf2c357 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5191,8 +5191,10 @@
     method public abstract void onClassifyText(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextClassification.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextClassification>);
     method public void onCreateTextClassificationSession(android.view.textclassifier.TextClassificationContext, android.view.textclassifier.TextClassificationSessionId);
     method public void onDestroyTextClassificationSession(android.view.textclassifier.TextClassificationSessionId);
+    method public void onDetectLanguage(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextLanguage.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLanguage>);
     method public abstract void onGenerateLinks(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextLinks.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>);
     method public void onSelectionEvent(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.SelectionEvent);
+    method public void onSuggestConversationActions(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.ConversationActions.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.ConversationActions>);
     method public abstract void onSuggestSelection(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextSelection.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.textclassifier.TextClassifierService";
   }
@@ -6254,6 +6256,45 @@
     field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
+  public class ImsMmTelManager {
+    method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(android.content.Context, int);
+    method public int getVoWiFiModeSetting();
+    method public boolean isAdvancedCallingSettingEnabled();
+    method public boolean isAvailable(int, int);
+    method public boolean isCapable(int, int);
+    method public boolean isVoWiFiRoamingSettingEnabled();
+    method public boolean isVoWiFiSettingEnabled();
+    method public boolean isVtSettingEnabled();
+    method public void registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+    method public void registerMmTelCapabilityCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.CapabilityCallback);
+    method public void setAdvancedCallingSetting(boolean);
+    method public void setRttCapabilitySetting(boolean);
+    method public void setVoWiFiModeSetting(int);
+    method public void setVoWiFiNonPersistent(boolean, int);
+    method public void setVoWiFiRoamingModeSetting(int);
+    method public void setVoWiFiRoamingSetting(boolean);
+    method public void setVoWiFiSetting(boolean);
+    method public void setVtSetting(boolean);
+    method public void unregisterImsRegistrationCallback(android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+    method public void unregisterMmTelCapabilityCallback(android.telephony.ims.ImsMmTelManager.CapabilityCallback);
+    field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
+    field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
+    field public static final int WIFI_MODE_WIFI_PREFERRED = 2; // 0x2
+  }
+
+  public static class ImsMmTelManager.CapabilityCallback {
+    ctor public ImsMmTelManager.CapabilityCallback();
+    method public void onCapabilitiesStatusChanged(android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
+  }
+
+  public static class ImsMmTelManager.RegistrationCallback {
+    ctor public ImsMmTelManager.RegistrationCallback();
+    method public void onDeregistered(android.telephony.ims.ImsReasonInfo);
+    method public void onRegistered(int);
+    method public void onRegistering(int);
+    method public void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
+  }
+
   public final class ImsReasonInfo implements android.os.Parcelable {
     ctor public ImsReasonInfo(int, int, java.lang.String);
     method public int describeContents();
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 1191e6a..020c443 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -48,8 +48,6 @@
 using android::Res_value;
 using android::ResStringPool;
 using android::ResTable_config;
-using android::String16;
-using android::String8;
 using android::StringPiece16;
 using android::base::StringPrintf;
 using android::idmap2::CommandLineOptions;
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index e5764f04..2dcf50f 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -12274,7 +12274,7 @@
 HPLorg/ccil/cowan/tagsoup/AttributesImpl;->setAttribute(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 HPLorg/ccil/cowan/tagsoup/Element;->anonymize()V
 HPLorg/ccil/cowan/tagsoup/Element;->preclose()V
-HPLorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V
+# HPLorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V b/76145463
 HPLorg/json/JSONArray;->optDouble(I)D
 HPLorg/json/JSONArray;->optDouble(ID)D
 HPLorg/json/JSONStringer;->value(J)Lorg/json/JSONStringer;
@@ -15623,7 +15623,7 @@
 HSPLandroid/app/admin/IDevicePolicyManager$Stub$Proxy;->isLockTaskPermitted(Ljava/lang/String;)Z
 HSPLandroid/app/admin/IDevicePolicyManager$Stub$Proxy;->isProvisioningAllowed(Ljava/lang/String;Ljava/lang/String;)Z
 HSPLandroid/app/admin/IDevicePolicyManager$Stub;-><init>()V
-HSPLandroid/app/admin/IDevicePolicyManager$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
+# HSPLandroid/app/admin/IDevicePolicyManager$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z b/76145463
 HSPLandroid/app/admin/IDevicePolicyManager;->addCrossProfileIntentFilter(Landroid/content/ComponentName;Landroid/content/IntentFilter;I)V
 HSPLandroid/app/admin/IDevicePolicyManager;->addCrossProfileWidgetProvider(Landroid/content/ComponentName;Ljava/lang/String;)Z
 HSPLandroid/app/admin/IDevicePolicyManager;->addOverrideApn(Landroid/content/ComponentName;Landroid/telephony/data/ApnSetting;)I
@@ -25254,7 +25254,7 @@
 HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->parseFromInfo(Landroid/media/MediaFormat;)V
 HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->parseWidthHeightRanges(Ljava/lang/Object;)Landroid/util/Pair;
 HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->supports(Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Number;)Z
-HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->updateLimits()V
+# HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->updateLimits()V b/76145463
 HSPLandroid/media/MediaCodecInfo;-><init>(Ljava/lang/String;Z[Landroid/media/MediaCodecInfo$CodecCapabilities;)V
 HSPLandroid/media/MediaCodecInfo;->access$200()Landroid/util/Range;
 HSPLandroid/media/MediaCodecInfo;->access$300()Landroid/util/Range;
@@ -37845,7 +37845,7 @@
 HSPLandroid/widget/ViewSwitcher;->getNextView()Landroid/view/View;
 HSPLandroid/widget/WrapperListAdapter;->getWrappedAdapter()Landroid/widget/ListAdapter;
 HSPLcom/android/i18n/phonenumbers/AlternateFormatsCountryCodeSet;->getCountryCodeSet()Ljava/util/Set;
-HSPLcom/android/i18n/phonenumbers/CountryCodeToRegionCodeMap;->getCountryCodeToRegionCodeMap()Ljava/util/Map;
+# HSPLcom/android/i18n/phonenumbers/CountryCodeToRegionCodeMap;->getCountryCodeToRegionCodeMap()Ljava/util/Map; b/76145463
 HSPLcom/android/i18n/phonenumbers/MetadataLoader;->loadMetadata(Ljava/lang/String;)Ljava/io/InputStream;
 HSPLcom/android/i18n/phonenumbers/MetadataManager$1;-><init>()V
 HSPLcom/android/i18n/phonenumbers/MetadataManager$1;->loadMetadata(Ljava/lang/String;)Ljava/io/InputStream;
@@ -37999,7 +37999,7 @@
 HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setCountryCodeSource(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
 HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setNationalNumber(J)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
 HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setRawInput(Ljava/lang/String;)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-HSPLcom/android/i18n/phonenumbers/ShortNumbersRegionCodeSet;->getRegionCodeSet()Ljava/util/Set;
+# HSPLcom/android/i18n/phonenumbers/ShortNumbersRegionCodeSet;->getRegionCodeSet()Ljava/util/Set; b/76145463
 HSPLcom/android/i18n/phonenumbers/internal/MatcherApi;->matchNationalNumber(Ljava/lang/CharSequence;Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;Z)Z
 HSPLcom/android/i18n/phonenumbers/internal/RegexBasedMatcher;->matchNationalNumber(Ljava/lang/CharSequence;Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;Z)Z
 HSPLcom/android/i18n/phonenumbers/internal/RegexCache$LRUCache$1;->removeEldestEntry(Ljava/util/Map$Entry;)Z
@@ -38978,7 +38978,7 @@
 HSPLcom/android/internal/os/BatteryStatsImpl$Uid;->updateUidProcessStateLocked(I)V
 HSPLcom/android/internal/os/BatteryStatsImpl$Uid;->writeJobCompletionsToParcelLocked(Landroid/os/Parcel;)V
 HSPLcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;->getUserIds()[I
-HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Landroid/os/Parcel;)V
+# HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Landroid/os/Parcel;)V b/76145463
 HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Ljava/io/File;Landroid/os/Handler;Lcom/android/internal/os/BatteryStatsImpl$PlatformIdleStateCallback;Lcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;)V
 HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Ljava/io/File;Landroid/os/Handler;Lcom/android/internal/os/BatteryStatsImpl$PlatformIdleStateCallback;Lcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;)V
 HSPLcom/android/internal/os/BatteryStatsImpl;->addHistoryBufferLocked(JBLandroid/os/BatteryStats$HistoryItem;)V
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 553acc8..83fab7e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1432,7 +1432,15 @@
         if (mAutoFillResetNeeded) {
             if (!mAutoFillIgnoreFirstResumePause) {
                 View focus = getCurrentFocus();
-                if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+                // On Activity rotation situation (mRestoredFromBundle is true),
+                // we should not call on AutofillManager in onResume()
+                // since the next Layout pass will do that.
+                // However, there are both cases where Activity#getCurrentFocus()
+                // will return null (window not preserved) and not null (window IS
+                // preserved), so we need to explicitly check for mRestoredFromBundle
+                // here.
+                if (!mRestoredFromBundle && focus != null
+                        && focus.canNotifyAutofillEnterExitEvent()) {
                     // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
                     // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
                     // window visibility after recreation is INVISIBLE in onResume() and next frame
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index 30d6bee..9ef24c6 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -27,6 +27,7 @@
 import dalvik.system.PathClassLoader;
 
 import java.util.Collection;
+import java.util.List;
 
 /** @hide */
 public class ApplicationLoaders {
@@ -38,15 +39,25 @@
     ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
                                String librarySearchPath, String libraryPermittedPath,
                                ClassLoader parent, String classLoaderName) {
+        return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
+                              librarySearchPath, libraryPermittedPath, parent, classLoaderName,
+                              null);
+    }
+
+    ClassLoader getClassLoaderWithSharedLibraries(
+            String zip, int targetSdkVersion, boolean isBundled,
+            String librarySearchPath, String libraryPermittedPath,
+            ClassLoader parent, String classLoaderName,
+            List<ClassLoader> sharedLibraries) {
         // For normal usage the cache key used is the same as the zip path.
         return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
-                              libraryPermittedPath, parent, zip, classLoaderName);
+                              libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
     }
 
     private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
                                        String librarySearchPath, String libraryPermittedPath,
                                        ClassLoader parent, String cacheKey,
-                                       String classLoaderName) {
+                                       String classLoaderName, List<ClassLoader> sharedLibraries) {
         /*
          * This is the parent we use if they pass "null" in.  In theory
          * this should be the "system" class loader; in practice we
@@ -75,7 +86,7 @@
 
                 ClassLoader classloader = ClassLoaderFactory.createClassLoader(
                         zip,  librarySearchPath, libraryPermittedPath, parent,
-                        targetSdkVersion, isBundled, classLoaderName);
+                        targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
 
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
@@ -90,7 +101,7 @@
 
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
             ClassLoader loader = ClassLoaderFactory.createClassLoader(
-                    zip, null, parent, classLoaderName);
+                    zip, null, parent, classLoaderName, sharedLibraries);
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             return loader;
         }
@@ -110,7 +121,7 @@
         // The cache key is passed separately to enable the stub WebView to be cached under the
         // stub's APK path, when the actual package path is the donor APK.
         return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
-                              cacheKey, null /* classLoaderName */);
+                              cacheKey, null /* classLoaderName */, null /* sharedLibraries */);
     }
 
     /**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9837deb..28ecb27 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1724,6 +1724,24 @@
     }
 
     @Override
+    public void updateServiceGroup(@NonNull ServiceConnection conn, int group, int importance) {
+        if (conn == null) {
+            throw new IllegalArgumentException("connection is null");
+        }
+        if (mPackageInfo != null) {
+            IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+                    getOuterContext(), conn);
+            try {
+                ActivityManager.getService().updateServiceGroup(sd, group, importance);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            throw new RuntimeException("Not supported in system context");
+        }
+    }
+
+    @Override
     public void unbindService(ServiceConnection conn) {
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f27c667..e83bcd0 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -140,6 +140,7 @@
     int bindIsolatedService(in IApplicationThread caller, in IBinder token, in Intent service,
             in String resolvedType, in IServiceConnection connection, int flags,
             in String instanceName, in String callingPackage, int userId);
+    void updateServiceGroup(in IServiceConnection connection, int group, int importance);
     boolean unbindService(in IServiceConnection connection);
     void publishService(in IBinder token, in Intent intent, in IBinder service);
     void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index da4f77b..3f10754 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -29,6 +29,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.dex.ArtManager;
 import android.content.pm.split.SplitDependencyLoader;
 import android.content.res.AssetManager;
@@ -70,8 +71,10 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 final class IntentReceiverLeaked extends AndroidRuntimeException {
     @UnsupportedAppUsage
@@ -397,6 +400,24 @@
         makePaths(activityThread, false, aInfo, outZipPaths, null);
     }
 
+    private static void appendSharedLibrariesLibPathsIfNeeded(
+            List<SharedLibraryInfo> sharedLibraries, ApplicationInfo aInfo,
+            Set<String> outSeenPaths,
+            List<String> outLibPaths) {
+        if (sharedLibraries == null) {
+            return;
+        }
+        for (SharedLibraryInfo lib : sharedLibraries) {
+            List<String> paths = lib.getAllCodePaths();
+            outSeenPaths.addAll(paths);
+            for (String path : paths) {
+                appendApkLibPathIfNeeded(path, aInfo, outLibPaths);
+            }
+            appendSharedLibrariesLibPathsIfNeeded(
+                    lib.getDependencies(), aInfo, outSeenPaths, outLibPaths);
+        }
+    }
+
     public static void makePaths(ActivityThread activityThread,
                                  boolean isBundledApp,
                                  ApplicationInfo aInfo,
@@ -404,7 +425,6 @@
                                  List<String> outLibPaths) {
         final String appDir = aInfo.sourceDir;
         final String libDir = aInfo.nativeLibraryDir;
-        final String[] sharedLibraries = aInfo.sharedLibraryFiles;
 
         outZipPaths.clear();
         outZipPaths.add(appDir);
@@ -499,11 +519,19 @@
             }
         }
 
-        // Prepend the shared libraries, maintaining their original order where possible.
-        if (sharedLibraries != null) {
+        // Add the shared libraries native paths. The dex files in shared libraries will
+        // be resolved through shared library loaders, which are setup later.
+        Set<String> outSeenPaths = new LinkedHashSet<>();
+        appendSharedLibrariesLibPathsIfNeeded(
+                aInfo.sharedLibraryInfos, aInfo, outSeenPaths, outLibPaths);
+
+        // ApplicationInfo.sharedLibraryFiles is a public API, so anyone can change it.
+        // We prepend shared libraries that the package manager hasn't seen, maintaining their
+        // original order where possible.
+        if (aInfo.sharedLibraryFiles != null) {
             int index = 0;
-            for (String lib : sharedLibraries) {
-                if (!outZipPaths.contains(lib)) {
+            for (String lib : aInfo.sharedLibraryFiles) {
+                if (!outSeenPaths.contains(lib) && !outZipPaths.contains(lib)) {
                     outZipPaths.add(index, lib);
                     index++;
                     appendApkLibPathIfNeeded(lib, aInfo, outLibPaths);
@@ -631,6 +659,43 @@
         return mSplitLoader.getSplitPathsForSplit(splitName);
     }
 
+    /**
+     * Create a class loader for the {@code sharedLibrary}. Shared libraries are canonicalized,
+     * so if we already created a class loader with that shared library, we return it.
+     *
+     * Implementation notes: the canonicalization of shared libraries is something dex2oat
+     * also does.
+     */
+    ClassLoader createSharedLibraryLoader(SharedLibraryInfo sharedLibrary,
+            boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) {
+        List<String> paths = sharedLibrary.getAllCodePaths();
+        List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
+                sharedLibrary.getDependencies(), isBundledApp, librarySearchPath,
+                libraryPermittedPath);
+        final String jars = (paths.size() == 1) ? paths.get(0) :
+                TextUtils.join(File.pathSeparator, paths);
+
+        // Shared libraries get a null parent: this has the side effect of having canonicalized
+        // shared libraries using ApplicationLoaders cache, which is the behavior we want.
+        return ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(jars,
+                    mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
+                    libraryPermittedPath, /* parent */ null,
+                    /* classLoaderName */ null, sharedLibraries);
+    }
+
+    private List<ClassLoader> createSharedLibrariesLoaders(List<SharedLibraryInfo> sharedLibraries,
+            boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) {
+        if (sharedLibraries == null) {
+            return null;
+        }
+        List<ClassLoader> loaders = new ArrayList<>();
+        for (SharedLibraryInfo info : sharedLibraries) {
+            loaders.add(createSharedLibraryLoader(
+                    info, isBundledApp, librarySearchPath, libraryPermittedPath));
+        }
+        return loaders;
+    }
+
     private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
         if (mPackageName.equals("android")) {
             // Note: This branch is taken for system server and we don't need to setup
@@ -759,10 +824,14 @@
             // as this is early and necessary.
             StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
 
-            mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
-                    mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
+            List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
+                    mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
+                    libraryPermittedPath);
+
+            mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
+                    zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
                     libraryPermittedPath, mBaseClassLoader,
-                    mApplicationInfo.classLoaderName);
+                    mApplicationInfo.classLoaderName, sharedLibraries);
             mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
 
             StrictMode.setThreadPolicy(oldPolicy);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 450efdf..f2a3e44 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3193,6 +3193,25 @@
     }
 
     /**
+     * Returns the actions that are contextual (marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) out
+     * of the actions in this notification.
+     *
+     * @hide
+     */
+    public List<Notification.Action> getContextualActions() {
+        if (actions == null) return Collections.emptyList();
+
+        List<Notification.Action> contextualActions = new ArrayList<>();
+        for (Notification.Action action : actions) {
+            if (action.getSemanticAction()
+                    == Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+                contextualActions.add(action);
+            }
+        }
+        return contextualActions;
+    }
+
+    /**
      * Builder class for {@link Notification} objects.
      *
      * Provides a convenient way to set the various fields of a {@link Notification} and generate
@@ -7461,7 +7480,11 @@
                 return mRemoteInputHistory;
             }
 
-            private Bundle toBundle() {
+            /**
+             * @hide
+             */
+            @VisibleForTesting
+            public Bundle toBundle() {
                 Bundle bundle = new Bundle();
                 if (mText != null) {
                     bundle.putCharSequence(KEY_TEXT, mText);
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index 3884a8d..a2dae3b 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -22,6 +22,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * Provides an immutable reference to an entity that appears repeatedly on different surfaces of the
  * platform. For example, this could represent the sender of a message.
@@ -121,6 +123,26 @@
     }
 
     @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Person) {
+            final Person other = (Person) obj;
+            return Objects.equals(mName, other.mName)
+                    && mIcon == null ? other.mIcon == null :
+                    (other.mIcon != null && mIcon.sameAs(other.mIcon))
+                    && Objects.equals(mUri, other.mUri)
+                    && Objects.equals(mKey, other.mKey)
+                    && mIsBot == other.mIsBot
+                    && mIsImportant == other.mIsImportant;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mName, mIcon, mUri, mKey, mIsBot, mIsImportant);
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 636b1b9..8d9d340 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -973,11 +973,11 @@
      */
     @UnsupportedAppUsage
     public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
-            int type) {
+            int type, String name) {
         final IBluetoothHeadset service = mService;
         if (service != null && isEnabled()) {
             try {
-                service.phoneStateChanged(numActive, numHeld, callState, number, type);
+                service.phoneStateChanged(numActive, numHeld, callState, number, type, name);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 004417b..cec8ef5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -425,6 +425,15 @@
      */
     public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
 
+    /**
+     * These bind flags reduce the strength of the binding such that we shouldn't
+     * consider it as pulling the process up to the level of the one that is bound to it.
+     * @hide
+     */
+    public static final int BIND_REDUCTION_FLAGS =
+            Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_WAIVE_PRIORITY
+                    | Context.BIND_ADJUST_BELOW_PERCEPTIBLE | Context.BIND_NOT_VISIBLE;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
             RECEIVER_VISIBLE_TO_INSTANT_APPS
@@ -2982,6 +2991,31 @@
     }
 
     /**
+     * For a service previously bound with {@link #bindService} or a related method, change
+     * how the system manages that service's process in relation to other processes.  This
+     * doesn't modify the original bind flags that were passed in when binding, but adjusts
+     * how the process will be managed in some cases based on those flags.  Currently only
+     * works on isolated processes (will be ignored for non-isolated processes).
+     *
+     * @param conn The connection interface previously supplied to bindService().  This
+     *             parameter must not be null.
+     * @param group A group to put this connection's process in.  Upon calling here, this
+     *              will override any previous group that was set for that process.  The group
+     *              tells the system about processes that are logically grouped together, so
+     *              should be managed as one unit of importance (such as when being considered
+     *              a recently used app).  All processes in the same app with the same group
+     *              are considered to be related.  Supplying 0 reverts to the default behavior
+     *              of not grouping.
+     * @param importance Additional importance of the processes within a group.  Upon calling
+     *                   here, this will override any previous group that was set for that
+     *                   process.  This fine-tunes process killing of all processes within
+     *                   a related groups -- higher importance values will be killed before
+     *                   lower ones.
+     */
+    public abstract void updateServiceGroup(@NonNull ServiceConnection conn, int group,
+            int importance);
+
+    /**
      * Disconnect from an application service.  You will no longer receive
      * calls as the service is restarted, and the service is now allowed to
      * stop at any time.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 88696b0e..2db44b4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -726,6 +726,11 @@
     }
 
     @Override
+    public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+        mBase.updateServiceGroup(conn, group, importance);
+    }
+
+    @Override
     public void unbindService(ServiceConnection conn) {
         mBase.unbindService(conn);
     }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 48240db..4e110da 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -45,6 +45,7 @@
 import java.text.Collator;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
 
@@ -830,7 +831,17 @@
      * the structure.
      */
     public String[] sharedLibraryFiles;
-    
+
+    /**
+     * List of all shared libraries this application is linked against.  This
+     * field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
+     * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving
+     * the structure.
+     *
+     * {@hide}
+     */
+    public List<SharedLibraryInfo> sharedLibraryInfos;
+
     /**
      * Full path to the default directory assigned to the package for its
      * persistent data.
@@ -1474,6 +1485,7 @@
         seInfo = orig.seInfo;
         seInfoUser = orig.seInfoUser;
         sharedLibraryFiles = orig.sharedLibraryFiles;
+        sharedLibraryInfos = orig.sharedLibraryInfos;
         dataDir = orig.dataDir;
         deviceProtectedDataDir = orig.deviceProtectedDataDir;
         credentialProtectedDataDir = orig.credentialProtectedDataDir;
@@ -1549,6 +1561,7 @@
         dest.writeString(seInfo);
         dest.writeString(seInfoUser);
         dest.writeStringArray(sharedLibraryFiles);
+        dest.writeTypedList(sharedLibraryInfos);
         dest.writeString(dataDir);
         dest.writeString(deviceProtectedDataDir);
         dest.writeString(credentialProtectedDataDir);
@@ -1621,6 +1634,7 @@
         seInfo = source.readString();
         seInfoUser = source.readString();
         sharedLibraryFiles = source.readStringArray();
+        sharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR);
         dataDir = source.readString();
         deviceProtectedDataDir = source.readString();
         credentialProtectedDataDir = source.readString();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 7ef5264..2fcf1dd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -7669,6 +7669,7 @@
         }
         if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
             ai.sharedLibraryFiles = p.usesLibraryFiles;
+            ai.sharedLibraryInfos = p.usesLibraryInfos;
         }
         if (state.stopped) {
             ai.flags |= ApplicationInfo.FLAG_STOPPED;
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 096301c..ad82626d 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -74,6 +74,7 @@
     private final String mPath;
     private final String mPackageName;
     private final String mName;
+    private final List<String> mCodePaths;
 
     private final long mVersion;
     private final @Type int mType;
@@ -84,6 +85,8 @@
     /**
      * Creates a new instance.
      *
+     * @param codePaths For a non {@link #TYPE_BUILTIN builtin} library, the locations of jars of
+     *                  this shared library. Null for builtin library.
      * @param name The lib name.
      * @param version The lib version if not builtin.
      * @param type The lib type.
@@ -92,11 +95,13 @@
      *
      * @hide
      */
-    public SharedLibraryInfo(String path, String packageName, String name, long version, int type,
+    public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
+            String name, long version, int type,
             VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
             List<SharedLibraryInfo> dependencies) {
         mPath = path;
         mPackageName = packageName;
+        mCodePaths = codePaths;
         mName = name;
         mVersion = version;
         mType = type;
@@ -106,7 +111,8 @@
     }
 
     private SharedLibraryInfo(Parcel parcel) {
-        this(parcel.readString(), parcel.readString(), parcel.readString(), parcel.readLong(),
+        this(parcel.readString(), parcel.readString(), parcel.readArrayList(null),
+                parcel.readString(), parcel.readLong(),
                 parcel.readInt(), parcel.readParcelable(null), parcel.readArrayList(null),
                 parcel.createTypedArrayList(SharedLibraryInfo.CREATOR));
     }
@@ -155,6 +161,25 @@
     }
 
     /**
+     * Get all code paths for that library.
+     *
+     * @return All code paths.
+     *
+     * @hide
+     */
+    public List<String> getAllCodePaths() {
+        if (getPath() != null) {
+            // Builtin library.
+            ArrayList<String> list = new ArrayList<>();
+            list.add(getPath());
+            return list;
+        } else {
+            // Static or dynamic library.
+            return mCodePaths;
+        }
+    }
+
+    /**
      * Add a library dependency to that library. Note that this
      * should be called under the package manager lock.
      *
@@ -273,6 +298,7 @@
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mPath);
         parcel.writeString(mPackageName);
+        parcel.writeList(mCodePaths);
         parcel.writeString(mName);
         parcel.writeLong(mVersion);
         parcel.writeInt(mType);
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 287a5ed..8160338 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.os.IThermalEventListener;
+import android.os.IThermalStatusListener;
 import android.os.Temperature;
 
 import java.util.List;
@@ -30,31 +31,60 @@
       * @param listener the IThermalEventListener to be notified.
       * {@hide}
       */
-    void registerThermalEventListener(in IThermalEventListener listener);
+    boolean registerThermalEventListener(in IThermalEventListener listener);
+
     /**
       * Register a listener for thermal events on given temperature type.
       * @param listener the IThermalEventListener to be notified.
       * @param type the temperature type IThermalEventListener to be notified.
+      * @return true if registered successfully.
       * {@hide}
       */
-    void registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
+    boolean registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
+
     /**
       * Unregister a previously-registered listener for thermal events.
       * @param listener the IThermalEventListener to no longer be notified.
+      * @return true if unregistered successfully.
       * {@hide}
       */
-    void unregisterThermalEventListener(in IThermalEventListener listener);
+    boolean unregisterThermalEventListener(in IThermalEventListener listener);
+
     /**
       * Get current temperature with its throttling status.
       * @return list of android.os.Temperature
       * {@hide}
       */
     List<Temperature> getCurrentTemperatures();
+
     /**
       * Get current temperature with its throttling status on given temperature type.
       * @param type the temperature type to query.
-      * @return list of android.os.Temperature
+      * @return list of {@link android.os.Temperature}.
       * {@hide}
       */
     List<Temperature> getCurrentTemperaturesWithType(in int type);
+
+    /**
+      * Register a listener for thermal status change.
+      * @param listener the IThermalStatusListener to be notified.
+      * @return true if registered successfully.
+      * {@hide}
+      */
+    boolean registerThermalStatusListener(in IThermalStatusListener listener);
+
+    /**
+      * Unregister a previously-registered listener for thermal status.
+      * @param listener the IThermalStatusListener to no longer be notified.
+      * @return true if unregistered successfully.
+      * {@hide}
+      */
+    boolean unregisterThermalStatusListener(in IThermalStatusListener listener);
+
+    /**
+      * Get current thermal status.
+      * @return status defined in {@link android.os.Temperature}.
+      * {@hide}
+      */
+    int getCurrentStatus();
 }
diff --git a/core/java/android/os/IThermalStatusListener.aidl b/core/java/android/os/IThermalStatusListener.aidl
new file mode 100644
index 0000000..a6da7d0
--- /dev/null
+++ b/core/java/android/os/IThermalStatusListener.aidl
@@ -0,0 +1,29 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+/**
+ * Listener for thermal status.
+ * {@hide}
+ */
+oneway interface IThermalStatusListener {
+    /**
+     * Called when overall thermal throttling status changed.
+     * @param status defined in {@link android.os.Temperature#ThrottlingStatus}.
+     */
+    void onStatusChange(int status);
+}
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index 37ed52c..bf85fbd 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -25,9 +25,7 @@
 
 /**
  * Temperature values used by IThermalService.
- */
-
-/**
+ *
  * @hide
  */
 public class Temperature implements Parcelable {
@@ -40,7 +38,6 @@
     /** The level of the sensor is currently in throttling */
     private int mStatus;
 
-    /** @hide */
     @IntDef(prefix = { "THROTTLING_" }, value = {
             THROTTLING_NONE,
             THROTTLING_LIGHT,
@@ -62,7 +59,6 @@
     public static final int THROTTLING_WARNING = ThrottlingSeverity.WARNING;
     public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN;
 
-    /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_UNKNOWN,
             TYPE_CPU,
@@ -95,19 +91,28 @@
      *
      * @return true if a temperature type is valid otherwise false.
      */
-    public static boolean isValidType(int type) {
+    public static boolean isValidType(@Type int type) {
         return type >= TYPE_UNKNOWN && type <= TYPE_BCL_PERCENTAGE;
     }
 
+    /**
+     * Verify a valid throttling status.
+     *
+     * @return true if a status is valid otherwise false.
+     */
+    public static boolean isValidStatus(@ThrottlingStatus int status) {
+        return status >= THROTTLING_NONE && status <= THROTTLING_SHUTDOWN;
+    }
+
     public Temperature() {
         this(Float.NaN, TYPE_UNKNOWN, "", THROTTLING_NONE);
     }
 
-    public Temperature(float value, @Type int type, String name, int status) {
+    public Temperature(float value, @Type int type, String name, @ThrottlingStatus int status) {
         mValue = value;
         mType = isValidType(type) ? type : TYPE_UNKNOWN;
         mName = name;
-        mStatus = status;
+        mStatus = isValidStatus(status) ? status : THROTTLING_NONE;
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index be3dd5d..943888b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12171,7 +12171,7 @@
         /**
          * Defines global runtime overrides to window policy.
          *
-         * See {@link com.android.server.policy.PolicyControl} for value format.
+         * See {@link com.android.server.wm.PolicyControl} for value format.
          *
          * @hide
          */
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index 09bba4b..e930f40 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.uicc.IccUtils;
 
@@ -26,7 +27,10 @@
 
 /**
  * Used to pass info to CarrierConfigService implementations so they can decide what values to
- * return.
+ * return. Instead of passing mcc, mnc, gid1, gid2, spn, imsi to locate carrier information,
+ * CarrierIdentifier also include carrier id {@link TelephonyManager#getSimCarrierId()},
+ * a platform-wide unique identifier for each carrier. CarrierConfigService can directly use
+ * carrier id as the key to look up the carrier info.
  */
 public class CarrierIdentifier implements Parcelable {
 
@@ -49,15 +53,40 @@
     private @Nullable String mImsi;
     private @Nullable String mGid1;
     private @Nullable String mGid2;
+    private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+    private int mPreciseCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
 
     public CarrierIdentifier(String mcc, String mnc, @Nullable String spn, @Nullable String imsi,
             @Nullable String gid1, @Nullable String gid2) {
+        this(mcc, mnc, spn, imsi, gid1, gid2, TelephonyManager.UNKNOWN_CARRIER_ID,
+                TelephonyManager.UNKNOWN_CARRIER_ID);
+    }
+
+    /**
+     * @param mcc mobile country code
+     * @param mnc mobile network code
+     * @param spn service provider name
+     * @param imsi International Mobile Subscriber Identity {@link TelephonyManager#getSubscriberId()}
+     * @param gid1 group id level 1 {@link TelephonyManager#getGroupIdLevel1()}
+     * @param gid2 group id level 2
+     * @param carrierid carrier unique identifier {@link TelephonyManager#getSimCarrierId()}, used
+     *                  to uniquely identify the carrier and look up the carrier configurations.
+     * @param preciseCarrierId precise carrier identifier {@link TelephonyManager#getSimPreciseCarrierId()}
+     * @hide
+     *
+     * TODO: expose this to public API
+     */
+    public CarrierIdentifier(String mcc, String mnc, @Nullable String spn,
+                             @Nullable String imsi, @Nullable String gid1, @Nullable String gid2,
+                             int carrierid, int preciseCarrierId) {
         mMcc = mcc;
         mMnc = mnc;
         mSpn = spn;
         mImsi = imsi;
         mGid1 = gid1;
         mGid2 = gid2;
+        mCarrierId = carrierid;
+        mPreciseCarrierId = preciseCarrierId;
     }
 
     /**
@@ -125,6 +154,22 @@
         return mGid2;
     }
 
+    /**
+     * Get the carrier id {@link TelephonyManager#getSimCarrierId() }
+     * @hide
+     */
+    public int getCarrierId() {
+        return mCarrierId;
+    }
+
+    /**
+     * Get the precise carrier id {@link TelephonyManager#getSimPreciseCarrierId()}
+     * @hide
+     */
+    public int getPreciseCarrierId() {
+        return mPreciseCarrierId;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -140,19 +185,14 @@
                 && Objects.equals(mSpn, that.mSpn)
                 && Objects.equals(mImsi, that.mImsi)
                 && Objects.equals(mGid1, that.mGid1)
-                && Objects.equals(mGid2, that.mGid2);
+                && Objects.equals(mGid2, that.mGid2)
+                && Objects.equals(mCarrierId, that.mCarrierId)
+                && Objects.equals(mPreciseCarrierId, that.mPreciseCarrierId);
     }
 
     @Override
-    public int hashCode() {
-        int result = 1;
-        result = 31 * result + Objects.hashCode(mMcc);
-        result = 31 * result + Objects.hashCode(mMnc);
-        result = 31 * result + Objects.hashCode(mSpn);
-        result = 31 * result + Objects.hashCode(mImsi);
-        result = 31 * result + Objects.hashCode(mGid1);
-        result = 31 * result + Objects.hashCode(mGid2);
-        return result;
+    public int hashCode(){
+        return Objects.hash(mMcc, mMnc, mSpn, mImsi, mGid1, mGid2, mCarrierId, mPreciseCarrierId);
     }
 
     @Override
@@ -168,18 +208,22 @@
         out.writeString(mImsi);
         out.writeString(mGid1);
         out.writeString(mGid2);
+        out.writeInt(mCarrierId);
+        out.writeInt(mPreciseCarrierId);
     }
 
     @Override
     public String toString() {
       return "CarrierIdentifier{"
-          + "mcc=" + mMcc
-          + ",mnc=" + mMnc
-          + ",spn=" + mSpn
-          + ",imsi=" + mImsi
-          + ",gid1=" + mGid1
-          + ",gid2=" + mGid2
-          + "}";
+              + "mcc=" + mMcc
+              + ",mnc=" + mMnc
+              + ",spn=" + mSpn
+              + ",imsi=" + mImsi
+              + ",gid1=" + mGid1
+              + ",gid2=" + mGid2
+              + ",carrierid=" + mCarrierId
+              + ",mPreciseCarrierId=" + mPreciseCarrierId
+              + "}";
     }
 
     /** @hide */
@@ -190,6 +234,8 @@
         mImsi = in.readString();
         mGid1 = in.readString();
         mGid2 = in.readString();
+        mCarrierId = in.readInt();
+        mPreciseCarrierId = in.readInt();
     }
 
     /** @hide */
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index b94ccf9..c351d89 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -93,7 +93,11 @@
      * </p>
      *
      * @param id contains details about the current carrier that can be used do decide what
-     *            configuration values to return.
+     *           configuration values to return. Instead of using details like MCCMNC to decide
+     *           current carrier, it also contains subscription carrier id
+     *           {@link android.telephony.TelephonyManager#getSimCarrierId()}, a platform-wide
+     *           unique identifier for each carrier, CarrierConfigService can directly use carrier
+     *           id as the key to look up the carrier info.
      * @return a {@link PersistableBundle} object containing the configuration or null if default
      *         values should be used.
      */
diff --git a/media/java/android/media/ISessionTokensListener.aidl b/core/java/android/service/textclassifier/IConversationActionsCallback.aidl
similarity index 63%
copy from media/java/android/media/ISessionTokensListener.aidl
copy to core/java/android/service/textclassifier/IConversationActionsCallback.aidl
index c83a19e..c35d424 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/android/service/textclassifier/IConversationActionsCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.service.textclassifier;
 
-import android.os.Bundle;
+import android.view.textclassifier.ConversationActions;
 
 /**
- * Listens for changes to the list of session tokens.
+ * Callback for a ConversationActions request.
  * @hide
  */
-oneway interface ISessionTokensListener {
-    void onSessionTokensChanged(in List<Bundle> tokens);
-}
+oneway interface IConversationActionsCallback {
+    void onSuccess(in ConversationActions conversationActions);
+    void onFailure();
+}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/ITextClassifierService.aidl b/core/java/android/service/textclassifier/ITextClassifierService.aidl
index 7ac72c7..254a710 100644
--- a/core/java/android/service/textclassifier/ITextClassifierService.aidl
+++ b/core/java/android/service/textclassifier/ITextClassifierService.aidl
@@ -16,14 +16,18 @@
 
 package android.service.textclassifier;
 
+import android.service.textclassifier.IConversationActionsCallback;
 import android.service.textclassifier.ITextClassificationCallback;
+import android.service.textclassifier.ITextLanguageCallback;
 import android.service.textclassifier.ITextLinksCallback;
 import android.service.textclassifier.ITextSelectionCallback;
+import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassificationSessionId;
 import android.view.textclassifier.TextLinks;
+import android.view.textclassifier.TextLanguage;
 import android.view.textclassifier.TextSelection;
 
 /**
@@ -58,4 +62,14 @@
 
     void onDestroyTextClassificationSession(
             in TextClassificationSessionId sessionId);
+
+    void onDetectLanguage(
+            in TextClassificationSessionId sessionId,
+            in TextLanguage.Request request,
+            in ITextLanguageCallback callback);
+
+    void onSuggestConversationActions(
+            in TextClassificationSessionId sessionId,
+            in ConversationActions.Request request,
+            in IConversationActionsCallback callback);
 }
diff --git a/media/java/android/media/ISessionTokensListener.aidl b/core/java/android/service/textclassifier/ITextLanguageCallback.aidl
similarity index 65%
rename from media/java/android/media/ISessionTokensListener.aidl
rename to core/java/android/service/textclassifier/ITextLanguageCallback.aidl
index c83a19e..263d99af 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/android/service/textclassifier/ITextLanguageCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.service.textclassifier;
 
-import android.os.Bundle;
+import android.view.textclassifier.TextLanguage;
 
 /**
- * Listens for changes to the list of session tokens.
+ * Callback for a TextLanguage request.
  * @hide
  */
-oneway interface ISessionTokensListener {
-    void onSessionTokensChanged(in List<Bundle> tokens);
-}
+oneway interface ITextLanguageCallback {
+    void onSuccess(in TextLanguage textLanguage);
+    void onFailure();
+}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 7f1082d..d7359f1 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -32,12 +32,14 @@
 import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassificationSessionId;
 import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLanguage;
 import android.view.textclassifier.TextLinks;
 import android.view.textclassifier.TextSelection;
 
@@ -92,8 +94,7 @@
         @Override
         public void onSuggestSelection(
                 TextClassificationSessionId sessionId,
-                TextSelection.Request request, ITextSelectionCallback callback)
-                throws RemoteException {
+                TextSelection.Request request, ITextSelectionCallback callback) {
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
             TextClassifierService.this.onSuggestSelection(
@@ -125,8 +126,7 @@
         @Override
         public void onClassifyText(
                 TextClassificationSessionId sessionId,
-                TextClassification.Request request, ITextClassificationCallback callback)
-                throws RemoteException {
+                TextClassification.Request request, ITextClassificationCallback callback) {
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
             TextClassifierService.this.onClassifyText(
@@ -156,8 +156,7 @@
         @Override
         public void onGenerateLinks(
                 TextClassificationSessionId sessionId,
-                TextLinks.Request request, ITextLinksCallback callback)
-                throws RemoteException {
+                TextLinks.Request request, ITextLinksCallback callback) {
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
             TextClassifierService.this.onGenerateLinks(
@@ -188,16 +187,81 @@
         @Override
         public void onSelectionEvent(
                 TextClassificationSessionId sessionId,
-                SelectionEvent event) throws RemoteException {
+                SelectionEvent event) {
             Preconditions.checkNotNull(event);
             TextClassifierService.this.onSelectionEvent(sessionId, event);
         }
 
         /** {@inheritDoc} */
         @Override
+        public void onDetectLanguage(
+                TextClassificationSessionId sessionId,
+                TextLanguage.Request request,
+                ITextLanguageCallback callback) {
+            Preconditions.checkNotNull(request);
+            Preconditions.checkNotNull(callback);
+            TextClassifierService.this.onDetectLanguage(
+                    sessionId,
+                    request,
+                    mCancellationSignal,
+                    new Callback<TextLanguage>() {
+                        @Override
+                        public void onSuccess(TextLanguage result) {
+                            try {
+                                callback.onSuccess(result);
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(CharSequence error) {
+                            try {
+                                callback.onFailure();
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        };
+                    });
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void onSuggestConversationActions(
+                TextClassificationSessionId sessionId,
+                ConversationActions.Request request,
+                IConversationActionsCallback callback) {
+            Preconditions.checkNotNull(request);
+            Preconditions.checkNotNull(callback);
+            TextClassifierService.this.onSuggestConversationActions(
+                    sessionId,
+                    request,
+                    mCancellationSignal,
+                    new Callback<ConversationActions>() {
+                        @Override
+                        public void onSuccess(ConversationActions result) {
+                            try {
+                                callback.onSuccess(result);
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(CharSequence error) {
+                            try {
+                                callback.onFailure();
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+                    });
+        }
+
+        /** {@inheritDoc} */
+        @Override
         public void onCreateTextClassificationSession(
-                TextClassificationContext context, TextClassificationSessionId sessionId)
-                throws RemoteException {
+                TextClassificationContext context, TextClassificationSessionId sessionId) {
             Preconditions.checkNotNull(context);
             Preconditions.checkNotNull(sessionId);
             TextClassifierService.this.onCreateTextClassificationSession(context, sessionId);
@@ -205,8 +269,7 @@
 
         /** {@inheritDoc} */
         @Override
-        public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId)
-                throws RemoteException {
+        public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId) {
             TextClassifierService.this.onDestroyTextClassificationSession(sessionId);
         }
     };
@@ -266,6 +329,38 @@
             @NonNull Callback<TextLinks> callback);
 
     /**
+     * Detects and returns the language of the give text.
+     *
+     * @param sessionId the session id
+     * @param request the language detection request
+     * @param cancellationSignal object to watch for canceling the current operation
+     * @param callback the callback to return the result to
+     */
+    public void onDetectLanguage(
+            @Nullable TextClassificationSessionId sessionId,
+            @NonNull TextLanguage.Request request,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<TextLanguage> callback) {
+        callback.onSuccess(getLocalTextClassifier().detectLanguage(request));
+    }
+
+    /**
+     * Suggests and returns a list of actions according to the given conversation.
+     *
+     * @param sessionId the session id
+     * @param request the conversation actions request
+     * @param cancellationSignal object to watch for canceling the current operation
+     * @param callback the callback to return the result to
+     */
+    public void onSuggestConversationActions(
+            @Nullable TextClassificationSessionId sessionId,
+            @NonNull ConversationActions.Request request,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<ConversationActions> callback) {
+        callback.onSuccess(getLocalTextClassifier().suggestConversationActions(request));
+    }
+
+    /**
      * Writes the selection event.
      * This is called when a selection event occurs. e.g. user changed selection; or smart selection
      * happened.
diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
index 3c3ef0c..a976d0e 100644
--- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -24,8 +24,8 @@
  * @hide
  */
 interface IWallpaperConnection {
-	void attachEngine(IWallpaperEngine engine);
-	void engineShown(IWallpaperEngine engine);
+    void attachEngine(IWallpaperEngine engine, int displayId);
+    void engineShown(IWallpaperEngine engine);
     ParcelFileDescriptor setWallpaper(String name);
     void onWallpaperColorsChanged(in WallpaperColors colors);
 }
diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl
index 5fd0157..99a81f5 100644
--- a/core/java/android/service/wallpaper/IWallpaperService.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperService.aidl
@@ -24,6 +24,6 @@
  */
 oneway interface IWallpaperService {
     void attach(IWallpaperConnection connection,
-    		IBinder windowToken, int windowType, boolean isPreview,
-    		int reqWidth, int reqHeight, in Rect padding);
+            IBinder windowToken, int windowType, boolean isPreview,
+            int reqWidth, int reqHeight, in Rect padding, int displayId);
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4bd86a4..6f51bec 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -24,7 +24,6 @@
 import android.app.WallpaperColors;
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
-import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -806,7 +805,7 @@
                                 com.android.internal.R.style.Animation_Wallpaper;
                         mInputChannel = new InputChannel();
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
-                                Display.DEFAULT_DISPLAY, mWinFrame, mContentInsets, mStableInsets,
+                                mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
                                 mOutsets, mDisplayCutout, mInputChannel) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
@@ -1015,7 +1014,15 @@
             if (mDestroyed) {
                 return;
             }
-            
+
+            mDisplayManager = getSystemService(DisplayManager.class);
+            mDisplay = mDisplayManager.getDisplay(wrapper.mDisplayId);
+            if (mDisplay == null) {
+                // TODO(b/115486823) Ignore this engine.
+                Log.e(TAG, "Attaching to a non-existent display: " + wrapper.mDisplayId);
+                return;
+            }
+
             mIWallpaperEngine = wrapper;
             mCaller = wrapper.mCaller;
             mConnection = wrapper.mConnection;
@@ -1027,10 +1034,7 @@
             mWindow.setSession(mSession);
 
             mLayout.packageName = getPackageName();
-
-            mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
             mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
-            mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
             mDisplayState = mDisplay.getState();
 
             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
@@ -1268,12 +1272,14 @@
         int mReqWidth;
         int mReqHeight;
         final Rect mDisplayPadding = new Rect();
+        int mDisplayId;
 
         Engine mEngine;
 
         IWallpaperEngineWrapper(WallpaperService context,
                 IWallpaperConnection conn, IBinder windowToken,
-                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
+                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
+                int displayId) {
             mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
             mConnection = conn;
             mWindowToken = windowToken;
@@ -1282,6 +1288,7 @@
             mReqWidth = reqWidth;
             mReqHeight = reqHeight;
             mDisplayPadding.set(padding);
+            mDisplayId = displayId;
             
             Message msg = mCaller.obtainMessage(DO_ATTACH);
             mCaller.sendMessage(msg);
@@ -1353,7 +1360,7 @@
             switch (message.what) {
                 case DO_ATTACH: {
                     try {
-                        mConnection.attachEngine(this);
+                        mConnection.attachEngine(this, mDisplayId);
                     } catch (RemoteException e) {
                         Log.w(TAG, "Wallpaper host disappeared", e);
                         return;
@@ -1453,9 +1460,10 @@
 
         @Override
         public void attach(IWallpaperConnection conn, IBinder windowToken,
-                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
+                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
+                int displayId) {
             new IWallpaperEngineWrapper(mTarget, conn, windowToken,
-                    windowType, isPreview, reqWidth, reqHeight, padding);
+                    windowType, isPreview, reqWidth, reqHeight, padding, displayId);
         }
     }
 
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 717a858..21c4252 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -21,9 +21,9 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.SystemClock;
 
-import libcore.util.CountryTimeZones;
-import libcore.util.CountryTimeZones.TimeZoneMapping;
-import libcore.util.TimeZoneFinder;
+import libcore.timezone.CountryTimeZones;
+import libcore.timezone.CountryTimeZones.TimeZoneMapping;
+import libcore.timezone.TimeZoneFinder;
 import libcore.util.ZoneInfoDB;
 
 import java.io.PrintWriter;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c836c9e..308a000 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -271,7 +271,7 @@
     /**
      * Called by the status bar to notify Views of changes to System UI visiblity.
      */
-    oneway void statusBarVisibilityChanged(int visibility);
+    oneway void statusBarVisibilityChanged(int displayId, int visibility);
 
     /**
      * Called by System UI to notify of changes to the visibility of Recents.
diff --git a/media/java/android/media/update/ProviderCreator.java b/core/java/android/view/textclassifier/ConversationActions.aidl
similarity index 74%
rename from media/java/android/media/update/ProviderCreator.java
rename to core/java/android/view/textclassifier/ConversationActions.aidl
index f5f3e47..fece939 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/core/java/android/view/textclassifier/ConversationActions.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.update;
+package android.view.textclassifier;
 
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
-    U createProvider(T instance);
-}
+parcelable ConversationActions;
+parcelable ConversationActions.Request;
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 16eb5af..f8fce62 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -23,8 +23,10 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.service.textclassifier.IConversationActionsCallback;
 import android.service.textclassifier.ITextClassificationCallback;
 import android.service.textclassifier.ITextClassifierService;
+import android.service.textclassifier.ITextLanguageCallback;
 import android.service.textclassifier.ITextLinksCallback;
 import android.service.textclassifier.ITextSelectionCallback;
 
@@ -76,7 +78,7 @@
             if (selection != null) {
                 return selection;
             }
-        } catch (RemoteException | InterruptedException e) {
+        } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error suggesting selection for text. Using fallback.", e);
         }
         return mFallback.suggestSelection(request);
@@ -97,7 +99,7 @@
             if (classification != null) {
                 return classification;
             }
-        } catch (RemoteException | InterruptedException e) {
+        } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error classifying text. Using fallback.", e);
         }
         return mFallback.classifyText(request);
@@ -124,7 +126,7 @@
             if (links != null) {
                 return links;
             }
-        } catch (RemoteException | InterruptedException e) {
+        } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error generating links. Using fallback.", e);
         }
         return mFallback.generateLinks(request);
@@ -142,6 +144,42 @@
         }
     }
 
+    @Override
+    public TextLanguage detectLanguage(TextLanguage.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+
+        try {
+            final TextLanguageCallback callback = new TextLanguageCallback();
+            mManagerService.onDetectLanguage(mSessionId, request, callback);
+            final TextLanguage textLanguage = callback.mReceiver.get();
+            if (textLanguage != null) {
+                return textLanguage;
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Error detecting language.", e);
+        }
+        return mFallback.detectLanguage(request);
+    }
+
+    @Override
+    public ConversationActions suggestConversationActions(ConversationActions.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+
+        try {
+            final ConversationActionsCallback callback = new ConversationActionsCallback();
+            mManagerService.onSuggestConversationActions(mSessionId, request, callback);
+            final ConversationActions conversationActions = callback.mReceiver.get();
+            if (conversationActions != null) {
+                return conversationActions;
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Error reporting selection event.", e);
+        }
+        return mFallback.suggestConversationActions(request);
+    }
+
     /**
      * @inheritDoc
      */
@@ -193,7 +231,7 @@
 
     private static final class TextSelectionCallback extends ITextSelectionCallback.Stub {
 
-        final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>();
+        final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>("textselection");
 
         @Override
         public void onSuccess(TextSelection selection) {
@@ -208,7 +246,8 @@
 
     private static final class TextClassificationCallback extends ITextClassificationCallback.Stub {
 
-        final ResponseReceiver<TextClassification> mReceiver = new ResponseReceiver<>();
+        final ResponseReceiver<TextClassification> mReceiver =
+                new ResponseReceiver<>("textclassification");
 
         @Override
         public void onSuccess(TextClassification classification) {
@@ -223,7 +262,7 @@
 
     private static final class TextLinksCallback extends ITextLinksCallback.Stub {
 
-        final ResponseReceiver<TextLinks> mReceiver = new ResponseReceiver<>();
+        final ResponseReceiver<TextLinks> mReceiver = new ResponseReceiver<>("textlinks");
 
         @Override
         public void onSuccess(TextLinks links) {
@@ -236,12 +275,48 @@
         }
     }
 
+    private static final class TextLanguageCallback extends ITextLanguageCallback.Stub {
+
+        final ResponseReceiver<TextLanguage> mReceiver = new ResponseReceiver<>("textlanguage");
+
+        @Override
+        public void onSuccess(TextLanguage textLanguage) {
+            mReceiver.onSuccess(textLanguage);
+        }
+
+        @Override
+        public void onFailure() {
+            mReceiver.onFailure();
+        }
+    }
+
+    private static final class ConversationActionsCallback
+            extends IConversationActionsCallback.Stub {
+
+        final ResponseReceiver<ConversationActions> mReceiver =
+                new ResponseReceiver<>("conversationaction");
+
+        @Override
+        public void onSuccess(ConversationActions conversationActions) {
+            mReceiver.onSuccess(conversationActions);
+        }
+
+        @Override
+        public void onFailure() {
+            mReceiver.onFailure();
+        }
+    }
+
     private static final class ResponseReceiver<T> {
 
         private final CountDownLatch mLatch = new CountDownLatch(1);
-
+        private final String mName;
         private T mResponse;
 
+        private ResponseReceiver(String name) {
+            mName = name;
+        }
+
         public void onSuccess(T response) {
             mResponse = response;
             mLatch.countDown();
@@ -253,13 +328,21 @@
         }
 
         @Nullable
-        public T get() throws InterruptedException {
+        public T get() {
             // If this is running on the main thread, do not block for a response.
             // The response will unfortunately be null and the TextClassifier should depend on its
             // fallback.
             // NOTE that TextClassifier calls should preferably always be called on a worker thread.
             if (Looper.myLooper() != Looper.getMainLooper()) {
-                mLatch.await(2, TimeUnit.SECONDS);
+                try {
+                    boolean success = mLatch.await(2, TimeUnit.SECONDS);
+                    if (!success) {
+                        Log.w(LOG_TAG, "Timeout in ResponseReceiver.get(): " + mName);
+                    }
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    Log.e(LOG_TAG, "Interrupted during ResponseReceiver.get(): " + mName, e);
+                }
             }
             return mResponse;
         }
diff --git a/media/java/android/media/update/ProviderCreator.java b/core/java/android/view/textclassifier/TextLanguage.aidl
similarity index 74%
copy from media/java/android/media/update/ProviderCreator.java
copy to core/java/android/view/textclassifier/TextLanguage.aidl
index f5f3e47..54e3cf9f 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/core/java/android/view/textclassifier/TextLanguage.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.update;
+package android.view.textclassifier;
 
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
-    U createProvider(T instance);
-}
+parcelable TextLanguage;
+parcelable TextLanguage.Request;
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index 387857f..c5bc45a 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -22,6 +22,8 @@
 import dalvik.system.DexClassLoader;
 import dalvik.system.PathClassLoader;
 
+import java.util.List;
+
 /**
  * Creates class loaders.
  *
@@ -37,6 +39,13 @@
             DelegateLastClassLoader.class.getName();
 
     /**
+     * Returns the name of the class for PathClassLoader.
+     */
+    public static String getPathClassLoaderName() {
+        return PATH_CLASS_LOADER_NAME;
+    }
+
+    /**
      * Returns true if {@code name} is a supported classloader. {@code name} must be a
      * binary name of a class, as defined by {@code Class.getName}.
      */
@@ -68,25 +77,43 @@
      * is created.
      */
     public static ClassLoader createClassLoader(String dexPath,
-            String librarySearchPath, ClassLoader parent, String classloaderName) {
+            String librarySearchPath, ClassLoader parent, String classloaderName,
+            List<ClassLoader> sharedLibraries) {
+        ClassLoader[] arrayOfSharedLibraries = (sharedLibraries == null)
+                ? null
+                : sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]);
         if (isPathClassLoaderName(classloaderName)) {
-            return new PathClassLoader(dexPath, librarySearchPath, parent);
+            return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries);
         } else if (isDelegateLastClassLoaderName(classloaderName)) {
-            return new DelegateLastClassLoader(dexPath, librarySearchPath, parent);
+            return new DelegateLastClassLoader(dexPath, librarySearchPath, parent,
+                    arrayOfSharedLibraries);
         }
 
         throw new AssertionError("Invalid classLoaderName: " + classloaderName);
     }
 
     /**
+     * Same as {@code createClassLoader} below, but passes a null list of shared
+     * libraries.
+     */
+    public static ClassLoader createClassLoader(String dexPath,
+            String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
+            int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) {
+        return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath,
+            parent, targetSdkVersion, isNamespaceShared, classLoaderName, null);
+    }
+
+
+    /**
      * Create a ClassLoader and initialize a linker-namespace for it.
      */
     public static ClassLoader createClassLoader(String dexPath,
             String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
-            int targetSdkVersion, boolean isNamespaceShared, String classloaderName) {
+            int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
+            List<ClassLoader> sharedLibraries) {
 
         final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
-                classloaderName);
+                classLoaderName, sharedLibraries);
 
         boolean isForVendor = false;
         for (String path : dexPath.split(":")) {
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index ade5d05..3861739 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -29,6 +29,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.function.Predicate;
 
 /**
  * Given a process, will iterate over the child threads of the process, and return the CPU usage
@@ -70,6 +71,11 @@
     private static final String THREAD_NAME_FILENAME = "comm";
 
     /**
+     * Glob pattern for the process directory names under {@code proc}
+     */
+    private static final String PROCESS_DIRECTORY_FILTER = "[0-9]*";
+
+    /**
      * Default process name when the name can't be read
      */
     private static final String DEFAULT_PROCESS_NAME = "unknown_process";
@@ -96,6 +102,18 @@
     private static final int NUM_BUCKETS = 8;
 
     /**
+     * Default predicate for what UIDs to check for when getting processes. This filters to only
+     * select system UIDs (1000-1999)
+     */
+    private static final Predicate<Integer> DEFAULT_UID_PREDICATE =
+            uid -> 1000 <= uid && uid < 2000;
+
+    /**
+     * Value returned when there was an error getting an integer ID value (e.g. PID, UID)
+     */
+    private static final int ID_ERROR = -1;
+
+    /**
      * Where the proc filesystem is mounted
      */
     private final Path mProcPath;
@@ -116,8 +134,13 @@
      */
     private final FrequencyBucketCreator mFrequencyBucketCreator;
 
+    private final Injector mInjector;
+
     private KernelCpuThreadReader() throws IOException {
-        this(DEFAULT_PROC_PATH, DEFAULT_INITIAL_TIME_IN_STATE_PATH);
+        this(
+                DEFAULT_PROC_PATH,
+                DEFAULT_INITIAL_TIME_IN_STATE_PATH,
+                new Injector());
     }
 
     /**
@@ -128,9 +151,13 @@
      * format
      */
     @VisibleForTesting
-    public KernelCpuThreadReader(Path procPath, Path initialTimeInStatePath) throws IOException {
+    public KernelCpuThreadReader(
+            Path procPath,
+            Path initialTimeInStatePath,
+            Injector injector) throws IOException {
         mProcPath = procPath;
         mProcTimeInStateReader = new ProcTimeInStateReader(initialTimeInStatePath);
+        mInjector = injector;
 
         // Copy mProcTimeInState's frequencies and initialize bucketing
         final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz();
@@ -154,6 +181,67 @@
     }
 
     /**
+     * Get the per-thread CPU usage of all processes belonging to UIDs between {@code [1000, 2000)}
+     */
+    @Nullable
+    public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() {
+        return getProcessCpuUsageByUids(DEFAULT_UID_PREDICATE);
+    }
+
+    /**
+     * Get the per-thread CPU usage of all processes belonging to a set of UIDs
+     *
+     * <p>This function will crawl through all process {@code proc} directories found by the pattern
+     * {@code /proc/[0-9]*}, and then check the UID using {@code /proc/$PID/status}. This takes
+     * approximately 500ms on a Pixel 2. Therefore, this method can be computationally expensive,
+     * and should not be called more than once an hour.
+     *
+     * @param uidPredicate only get usage from processes owned by UIDs that match this predicate
+     */
+    @Nullable
+    public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids(Predicate<Integer> uidPredicate) {
+        if (DEBUG) {
+            Slog.d(TAG, "Reading CPU thread usages for processes owned by UIDs");
+        }
+
+        final ArrayList<ProcessCpuUsage> processCpuUsages = new ArrayList<>();
+
+        try (DirectoryStream<Path> processPaths =
+                     Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) {
+            for (Path processPath : processPaths) {
+                final int processId = getProcessId(processPath);
+                final int uid = mInjector.getUidForPid(processId);
+                if (uid == ID_ERROR || processId == ID_ERROR) {
+                    continue;
+                }
+                if (!uidPredicate.test(uid)) {
+                    continue;
+                }
+
+                final ProcessCpuUsage processCpuUsage =
+                        getProcessCpuUsage(processPath, processId, uid);
+                if (processCpuUsage != null) {
+                    processCpuUsages.add(processCpuUsage);
+                }
+            }
+        } catch (IOException e) {
+            Slog.w("Failed to iterate over process paths", e);
+            return null;
+        }
+
+        if (processCpuUsages.isEmpty()) {
+            Slog.w(TAG, "Didn't successfully get any process CPU information for UIDs specified");
+            return null;
+        }
+
+        if (DEBUG) {
+            Slog.d(TAG, "Read usage for " + processCpuUsages.size() + " processes");
+        }
+
+        return processCpuUsages;
+    }
+
+    /**
      * Read all of the CPU usage statistics for each child thread of the current process
      *
      * @return process CPU usage containing usage of all child threads
@@ -162,8 +250,8 @@
     public ProcessCpuUsage getCurrentProcessCpuUsage() {
         return getProcessCpuUsage(
                 mProcPath.resolve("self"),
-                Process.myPid(),
-                Process.myUid());
+                mInjector.myPid(),
+                mInjector.myUid());
     }
 
     /**
@@ -172,7 +260,8 @@
      * @param processPath the {@code /proc} path of the thread
      * @param processId the ID of the process
      * @param uid the ID of the user who owns the process
-     * @return process CPU usage containing usage of all child threads
+     * @return process CPU usage containing usage of all child threads. Null if the process exited
+     * and its {@code proc} directory was removed while collecting information
      */
     @Nullable
     private ProcessCpuUsage getProcessCpuUsage(Path processPath, int processId, int uid) {
@@ -224,7 +313,8 @@
      * Get a thread's CPU usage
      *
      * @param threadDirectory the {@code /proc} directory of the thread
-     * @return null in the case that the directory read failed
+     * @return thread CPU usage. Null if the thread exited and its {@code proc} directory was
+     * removed while collecting information
      */
     @Nullable
     private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) {
@@ -280,6 +370,22 @@
     }
 
     /**
+     * Get the ID of a process from its path
+     *
+     * @param processPath {@code proc} path of the process
+     * @return the ID, {@link #ID_ERROR} if the path could not be parsed
+     */
+    private int getProcessId(Path processPath) {
+        String fileName = processPath.getFileName().toString();
+        try {
+            return Integer.parseInt(fileName);
+        } catch (NumberFormatException e) {
+            Slog.w(TAG, "Failed to parse " + fileName + " as process ID", e);
+            return ID_ERROR;
+        }
+    }
+
+    /**
      * Puts frequencies and usage times into buckets
      */
     @VisibleForTesting
@@ -443,4 +549,31 @@
             this.usageTimesMillis = usageTimesMillis;
         }
     }
+
+    /**
+     * Used to inject static methods from {@link Process}
+     */
+    @VisibleForTesting
+    public static class Injector {
+        /**
+         * Get the PID of the current process
+         */
+        public int myPid() {
+            return Process.myPid();
+        }
+
+        /**
+         * Get the UID that owns the current process
+         */
+        public int myUid() {
+            return Process.myUid();
+        }
+
+        /**
+         * Get the UID for the process with ID {@code pid}
+         */
+        public int getUidForPid(int pid) {
+            return Process.getUidForPid(pid);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/ProcTimeInStateReader.java b/core/java/com/android/internal/os/ProcTimeInStateReader.java
index 3a63498..2318473 100644
--- a/core/java/com/android/internal/os/ProcTimeInStateReader.java
+++ b/core/java/com/android/internal/os/ProcTimeInStateReader.java
@@ -19,9 +19,15 @@
 import android.annotation.Nullable;
 import android.os.Process;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Reads and parses {@code time_in_state} files in the {@code proc} filesystem.
@@ -43,6 +49,17 @@
  *
  * This file would indicate that the CPU has spent 30 milliseconds at frequency 300,000KHz (300Mhz)
  * and 10 milliseconds at frequency 1,900,800KHz (1.9GHz).
+ *
+ * <p>This class will also read {@code time_in_state} files with headers, such as:
+ * <pre>
+ *   cpu0
+ *   300000 3
+ *   364800 0
+ *   ...
+ *   cpu4
+ *   300000 1
+ *   364800 4
+ * </pre>
  */
 public class ProcTimeInStateReader {
     private static final String TAG = "ProcTimeInStateReader";
@@ -51,24 +68,28 @@
      * The format of a single line of the {@code time_in_state} file that exports the frequency
      * values
      */
-    private static final int[] TIME_IN_STATE_LINE_FREQUENCY_FORMAT = {
+    private static final List<Integer> TIME_IN_STATE_LINE_FREQUENCY_FORMAT = Arrays.asList(
             Process.PROC_OUT_LONG | Process.PROC_SPACE_TERM,
-            Process.PROC_NEWLINE_TERM,
-    };
+            Process.PROC_NEWLINE_TERM
+    );
 
     /**
      * The format of a single line of the {@code time_in_state} file that exports the time values
      */
-    private static final int[] TIME_IN_STATE_LINE_TIME_FORMAT = {
+    private static final List<Integer> TIME_IN_STATE_LINE_TIME_FORMAT = Arrays.asList(
             Process.PROC_SPACE_TERM,
-            Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM,
-    };
+            Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM
+    );
 
     /**
-     * The format of the {@code time_in_state} file, defined using {@link Process}'s {@code
-     * PROC_OUT_LONG} and related variables
-     *
-     * Defined on first successful read of {@code time_in_state} file.
+     * The format of a header line of the {@code time_in_state} file
+     */
+    private static final List<Integer> TIME_IN_STATE_HEADER_LINE_FORMAT =
+            Collections.singletonList(Process.PROC_NEWLINE_TERM);
+
+    /**
+     * The format of the {@code time_in_state} file to extract times, defined using {@link
+     * Process}'s {@code PROC_OUT_LONG} and related variables
      */
     private int[] mTimeInStateTimeFormat;
 
@@ -141,46 +162,44 @@
         // Read the bytes of the `time_in_state` file
         byte[] timeInStateBytes = Files.readAllBytes(timeInStatePath);
 
-        // The number of lines in the `time_in_state` file is the number of frequencies available
+        // Iterate over the lines of the time_in_state file, for each one adding a line to the
+        // formats. These formats are used to extract either the frequencies or the times from a
+        // time_in_state file
+        // Also check if each line is a header, and handle this in the created format arrays
+        ArrayList<Integer> timeInStateFrequencyFormat = new ArrayList<>();
+        ArrayList<Integer> timeInStateTimeFormat = new ArrayList<>();
         int numFrequencies = 0;
         for (int i = 0; i < timeInStateBytes.length; i++) {
-            if (timeInStateBytes[i] == '\n') {
+            // If the first character of the line is not a digit, we treat it as a header
+            if (!Character.isDigit(timeInStateBytes[i])) {
+                timeInStateFrequencyFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT);
+                timeInStateTimeFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT);
+            } else {
+                timeInStateFrequencyFormat.addAll(TIME_IN_STATE_LINE_FREQUENCY_FORMAT);
+                timeInStateTimeFormat.addAll(TIME_IN_STATE_LINE_TIME_FORMAT);
                 numFrequencies++;
             }
-        }
-        if (numFrequencies == 0) {
-            throw new IOException("Empty time_in_state file");
+            // Go to the next line
+            while (i < timeInStateBytes.length && timeInStateBytes[i] != '\n') {
+                i++;
+            }
         }
 
-        // Set `mTimeInStateTimeFormat` and `timeInStateFrequencyFormat` to the correct length, and
-        // then copy in the `TIME_IN_STATE_{FREQUENCY,TIME}_LINE_FORMAT` until it's full. As we only
-        // use the frequency format in this method, it is not an member variable.
-        final int[] timeInStateTimeFormat =
-                new int[numFrequencies * TIME_IN_STATE_LINE_TIME_FORMAT.length];
-        final int[] timeInStateFrequencyFormat =
-                new int[numFrequencies * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length];
-        for (int i = 0; i < numFrequencies; i++) {
-            System.arraycopy(
-                    TIME_IN_STATE_LINE_FREQUENCY_FORMAT, 0, timeInStateFrequencyFormat,
-                    i * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length,
-                    TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length);
-            System.arraycopy(
-                    TIME_IN_STATE_LINE_TIME_FORMAT, 0, timeInStateTimeFormat,
-                    i * TIME_IN_STATE_LINE_TIME_FORMAT.length,
-                    TIME_IN_STATE_LINE_TIME_FORMAT.length);
+        if (numFrequencies == 0) {
+            throw new IOException("Empty time_in_state file");
         }
 
         // Read the frequencies from the `time_in_state` file and store them, as they will be the
         // same for every `time_in_state` file
         final long[] readLongs = new long[numFrequencies];
         final boolean readSuccess = Process.parseProcLine(
-                timeInStateBytes, 0, timeInStateBytes.length, timeInStateFrequencyFormat,
-                null, readLongs, null);
+                timeInStateBytes, 0, timeInStateBytes.length,
+                ArrayUtils.convertToIntArray(timeInStateFrequencyFormat), null, readLongs, null);
         if (!readSuccess) {
             throw new IOException("Failed to parse time_in_state file");
         }
 
-        mTimeInStateTimeFormat = timeInStateTimeFormat;
+        mTimeInStateTimeFormat = ArrayUtils.convertToIntArray(timeInStateTimeFormat);
         mFrequenciesKhz = readLongs;
     }
 }
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index ccbe0ee..49d5007 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -692,14 +692,8 @@
 
 static long get_allocated_vmalloc_memory() {
     char line[1024];
-    // Ignored tags that don't actually consume memory (ie remappings)
-    static const char* const ignored_tags[] = {
-            "ioremap",
-            "map_lowmem",
-            "vm_map_ram",
-            NULL
-    };
-    long size, vmalloc_allocated_size = 0;
+
+    long vmalloc_allocated_size = 0;
 
     UniqueFile fp = MakeUniqueFile("/proc/vmallocinfo", "re");
     if (fp == nullptr) {
@@ -710,17 +704,15 @@
         if (fgets(line, 1024, fp.get()) == NULL) {
             break;
         }
-        bool valid_line = true;
-        int i = 0;
-        while (ignored_tags[i]) {
-            if (strstr(line, ignored_tags[i]) != NULL) {
-                valid_line = false;
-                break;
-            }
-            i++;
+
+        // check to see if there are pages mapped in vmalloc area
+        if (!strstr(line, "pages=")) {
+            continue;
         }
-        if (valid_line && (sscanf(line, "%*x-%*x %ld", &size) == 1)) {
-            vmalloc_allocated_size += size;
+
+        long nr_pages;
+        if (sscanf(line, "%*x-%*x %*ld %*s pages=%ld", &nr_pages) == 1) {
+            vmalloc_allocated_size += (nr_pages * getpagesize());
         }
     }
     return vmalloc_allocated_size;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index b0dbaa0..99f096d 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -97,7 +97,7 @@
 message WindowManagerPolicyProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    optional int32 last_system_ui_flags = 1;
+    optional int32 last_system_ui_flags = 1 [deprecated=true];
     enum UserRotationMode {
         USER_ROTATION_FREE = 0;
         USER_ROTATION_LOCKED = 1;
@@ -108,18 +108,18 @@
     optional bool screen_on_fully = 5;
     optional bool keyguard_draw_complete = 6;
     optional bool window_manager_draw_complete = 7;
-    optional string focused_app_token = 8;
-    optional IdentifierProto focused_window = 9;
-    optional IdentifierProto top_fullscreen_opaque_window = 10;
-    optional IdentifierProto top_fullscreen_opaque_or_dimming_window = 11;
+    optional string focused_app_token = 8 [deprecated=true];
+    optional IdentifierProto focused_window = 9 [deprecated=true];
+    optional IdentifierProto top_fullscreen_opaque_window = 10 [deprecated=true];
+    optional IdentifierProto top_fullscreen_opaque_or_dimming_window = 11 [deprecated=true];
     optional bool keyguard_occluded = 12;
     optional bool keyguard_occluded_changed = 13;
     optional bool keyguard_occluded_pending = 14;
-    optional bool force_status_bar = 15;
-    optional bool force_status_bar_from_keyguard = 16;
-    optional BarControllerProto status_bar = 17;
-    optional BarControllerProto navigation_bar = 18;
-    optional WindowOrientationListenerProto orientation_listener = 19;
+    optional bool force_status_bar = 15 [deprecated=true];
+    optional bool force_status_bar_from_keyguard = 16 [deprecated=true];
+    optional BarControllerProto status_bar = 17 [deprecated=true];
+    optional BarControllerProto navigation_bar = 18 [deprecated=true];
+    optional WindowOrientationListenerProto orientation_listener = 19 [deprecated=true];
     optional KeyguardServiceDelegateProto keyguard_delegate = 20;
 }
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 3a33d57..46aa5b4 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -16,12 +16,9 @@
 
 package android.view.textclassifier;
 
-import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.any;
@@ -41,315 +38,24 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentMatcher;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class TextClassificationManagerTest {
 
     private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
-    private static final String NO_TYPE = null;
 
     private Context mContext;
     private TextClassificationManager mTcm;
-    private TextClassifier mClassifier;
 
     @Before
     public void setup() {
         mContext = InstrumentationRegistry.getTargetContext();
         mTcm = mContext.getSystemService(TextClassificationManager.class);
-        // Test with the local textClassifier only. (We only bundle "en" model by default).
-        // It's hard to reliably test the results of the device's TextClassifierServiceImpl here.
-        mClassifier = mTcm.getTextClassifier(TextClassifier.LOCAL);
-    }
-
-    @Test
-    public void testSmartSelection() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Contact me at droid@android.com";
-        String selected = "droid";
-        String suggested = "droid@android.com";
-        int startIndex = text.indexOf(selected);
-        int endIndex = startIndex + selected.length();
-        int smartStartIndex = text.indexOf(suggested);
-        int smartEndIndex = smartStartIndex + suggested.length();
-        TextSelection.Request request = new TextSelection.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextSelection selection = mClassifier.suggestSelection(request);
-        assertThat(selection,
-                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
-    }
-
-    @Test
-    public void testSmartSelection_url() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Visit http://www.android.com for more information";
-        String selected = "http";
-        String suggested = "http://www.android.com";
-        int startIndex = text.indexOf(selected);
-        int endIndex = startIndex + selected.length();
-        int smartStartIndex = text.indexOf(suggested);
-        int smartEndIndex = smartStartIndex + suggested.length();
-        TextSelection.Request request = new TextSelection.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextSelection selection = mClassifier.suggestSelection(request);
-        assertThat(selection,
-                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
-    }
-
-    @Test
-    public void testSmartSelection_withEmoji() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "\uD83D\uDE02 Hello.";
-        String selected = "Hello";
-        int startIndex = text.indexOf(selected);
-        int endIndex = startIndex + selected.length();
-        TextSelection.Request request = new TextSelection.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextSelection selection = mClassifier.suggestSelection(request);
-        assertThat(selection,
-                isTextSelection(startIndex, endIndex, NO_TYPE));
-    }
-
-    @Test
-    public void testClassifyText() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Contact me at droid@android.com";
-        String classifiedText = "droid@android.com";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_EMAIL));
-    }
-
-    @Test
-    public void testTextClassifyText_url() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Visit www.android.com for more information";
-        String classifiedText = "www.android.com";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
-    }
-
-    @Test
-    public void testTextClassifyText_address() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Brandschenkestrasse 110, Zürich, Switzerland";
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, 0, text.length())
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification, isTextClassification(text, TextClassifier.TYPE_ADDRESS));
-    }
-
-    @Test
-    public void testTextClassifyText_url_inCaps() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Visit HTTP://ANDROID.COM for more information";
-        String classifiedText = "HTTP://ANDROID.COM";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
-    }
-
-    @Test
-    public void testTextClassifyText_date() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Let's meet on January 9, 2018.";
-        String classifiedText = "January 9, 2018";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_DATE));
-    }
-
-    @Test
-    public void testTextClassifyText_datetime() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Let's meet 2018/01/01 10:30:20.";
-        String classifiedText = "2018/01/01 10:30:20";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification,
-                isTextClassification(classifiedText, TextClassifier.TYPE_DATE_TIME));
-    }
-
-    @Test
-    public void testGenerateLinks_phone() {
-        if (isTextClassifierDisabled()) return;
-        String text = "The number is +12122537077. See you tonight!";
-        TextLinks.Request request = new TextLinks.Request.Builder(text).build();
-        assertThat(mClassifier.generateLinks(request),
-                isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE));
-    }
-
-    @Test
-    public void testGenerateLinks_exclude() {
-        if (isTextClassifierDisabled()) return;
-        String text = "You want apple@banana.com. See you tonight!";
-        List<String> hints = Collections.EMPTY_LIST;
-        List<String> included = Collections.EMPTY_LIST;
-        List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
-        TextLinks.Request request = new TextLinks.Request.Builder(text)
-                .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
-                .setDefaultLocales(LOCALES)
-                .build();
-        assertThat(mClassifier.generateLinks(request),
-                not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
-    }
-
-    @Test
-    public void testGenerateLinks_explicit_address() {
-        if (isTextClassifierDisabled()) return;
-        String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
-        List<String> explicit = Arrays.asList(TextClassifier.TYPE_ADDRESS);
-        TextLinks.Request request = new TextLinks.Request.Builder(text)
-                .setEntityConfig(TextClassifier.EntityConfig.createWithExplicitEntityList(explicit))
-                .setDefaultLocales(LOCALES)
-                .build();
-        assertThat(mClassifier.generateLinks(request),
-                isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
-                        TextClassifier.TYPE_ADDRESS));
-    }
-
-    @Test
-    public void testGenerateLinks_exclude_override() {
-        if (isTextClassifierDisabled()) return;
-        String text = "You want apple@banana.com. See you tonight!";
-        List<String> hints = Collections.EMPTY_LIST;
-        List<String> included = Arrays.asList(TextClassifier.TYPE_EMAIL);
-        List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
-        TextLinks.Request request = new TextLinks.Request.Builder(text)
-                .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
-                .setDefaultLocales(LOCALES)
-                .build();
-        assertThat(mClassifier.generateLinks(request),
-                not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
-    }
-
-    @Test
-    public void testGenerateLinks_maxLength() {
-        if (isTextClassifierDisabled()) return;
-        char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength()];
-        Arrays.fill(manySpaces, ' ');
-        TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
-        TextLinks links = mClassifier.generateLinks(request);
-        assertTrue(links.getLinks().isEmpty());
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testGenerateLinks_tooLong() {
-        if (isTextClassifierDisabled()) {
-            throw new IllegalArgumentException("pass if disabled");
-        }
-        char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
-        Arrays.fill(manySpaces, ' ');
-        TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
-        mClassifier.generateLinks(request);
-    }
-
-    @Test
-    public void testDetectLanguage() {
-        if (isTextClassifierDisabled()) return;
-        String text = "This is English text";
-        TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
-        TextLanguage textLanguage = mClassifier.detectLanguage(request);
-        assertThat(textLanguage, isTextLanguage("en"));
-    }
-
-    @Test
-    public void testDetectLanguage_japanese() {
-        if (isTextClassifierDisabled()) return;
-        String text = "これは日本語のテキストです";
-        TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
-        TextLanguage textLanguage = mClassifier.detectLanguage(request);
-        assertThat(textLanguage, isTextLanguage("ja"));
-    }
-
-    @Test
-    public void testSuggestConversationActions_textReplyOnly_maxThree() {
-        if (isTextClassifierDisabled()) return;
-        ConversationActions.Message message =
-                new ConversationActions.Message.Builder().setText("Hello").build();
-        ConversationActions.TypeConfig typeConfig =
-                new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
-                        .setIncludedTypes(
-                                Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
-                        .build();
-        ConversationActions.Request request =
-                new ConversationActions.Request.Builder(Collections.singletonList(message))
-                        .setMaxSuggestions(1)
-                        .setTypeConfig(typeConfig)
-                        .build();
-
-        ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
-        assertTrue(conversationActions.getConversationActions().size() <= 1);
-        for (ConversationActions.ConversationAction conversationAction :
-                conversationActions.getConversationActions()) {
-            assertEquals(conversationAction.getType(), ConversationActions.TYPE_TEXT_REPLY);
-            assertNotNull(conversationAction.getTextReply());
-            assertTrue(conversationAction.getConfidenceScore() > 0);
-            assertTrue(conversationAction.getConfidenceScore() <= 1);
-        }
     }
 
     @Test
@@ -411,102 +117,4 @@
         assertFalse(result.getActions().isEmpty());
         assertNotSame(result, fallbackResult);
     }
-
-    private boolean isTextClassifierDisabled() {
-        return mClassifier == TextClassifier.NO_OP;
-    }
-
-    private static Matcher<TextSelection> isTextSelection(
-            final int startIndex, final int endIndex, final String type) {
-        return new BaseMatcher<TextSelection>() {
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof TextSelection) {
-                    TextSelection selection = (TextSelection) o;
-                    return startIndex == selection.getSelectionStartIndex()
-                            && endIndex == selection.getSelectionEndIndex()
-                            && typeMatches(selection, type);
-                }
-                return false;
-            }
-
-            private boolean typeMatches(TextSelection selection, String type) {
-                return type == null
-                        || (selection.getEntityCount() > 0
-                                && type.trim().equalsIgnoreCase(selection.getEntity(0)));
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendValue(
-                        String.format("%d, %d, %s", startIndex, endIndex, type));
-            }
-        };
-    }
-
-    private static Matcher<TextLinks> isTextLinksContaining(
-            final String text, final String substring, final String type) {
-        return new BaseMatcher<TextLinks>() {
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("text=").appendValue(text)
-                        .appendText(", substring=").appendValue(substring)
-                        .appendText(", type=").appendValue(type);
-            }
-
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof TextLinks) {
-                    for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) {
-                        if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) {
-                            return type.equals(link.getEntity(0));
-                        }
-                    }
-                }
-                return false;
-            }
-        };
-    }
-
-    private static Matcher<TextClassification> isTextClassification(
-            final String text, final String type) {
-        return new BaseMatcher<TextClassification>() {
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof TextClassification) {
-                    TextClassification result = (TextClassification) o;
-                    return text.equals(result.getText())
-                            && result.getEntityCount() > 0
-                            && type.equals(result.getEntity(0));
-                }
-                return false;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("text=").appendValue(text)
-                        .appendText(", type=").appendValue(type);
-            }
-        };
-    }
-
-    private static Matcher<TextLanguage> isTextLanguage(final String languageTag) {
-        return new BaseMatcher<TextLanguage>() {
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof TextLanguage) {
-                    TextLanguage result = (TextLanguage) o;
-                    return result.getLocaleHypothesisCount() > 0
-                            && languageTag.equals(result.getLocale(0).toLanguageTag());
-                }
-                return false;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("locale=").appendValue(languageTag);
-            }
-        };
-    }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
new file mode 100644
index 0000000..06ba15e
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -0,0 +1,454 @@
+/*
+ * 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.view.textclassifier;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.LocaleList;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Testing {@link TextClassifierTest} APIs on local and system textclassifier.
+ * <p>
+ * Tests are skipped if such a textclassifier does not exist.
+ */
+@SmallTest
+@RunWith(Parameterized.class)
+public class TextClassifierTest {
+    private static final String LOCAL = "local";
+    private static final String SYSTEM = "system";
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Iterable<Object> textClassifierTypes() {
+        return Arrays.asList(LOCAL, SYSTEM);
+    }
+
+    @Parameterized.Parameter
+    public String mTextClassifierType;
+
+    private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
+    private static final String NO_TYPE = null;
+
+    private Context mContext;
+    private TextClassificationManager mTcm;
+    private TextClassifier mClassifier;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mTcm = mContext.getSystemService(TextClassificationManager.class);
+        mClassifier = mTcm.getTextClassifier(
+                mTextClassifierType.equals(LOCAL) ? TextClassifier.LOCAL : TextClassifier.SYSTEM);
+    }
+
+    @Test
+    public void testSmartSelection() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Contact me at droid@android.com";
+        String selected = "droid";
+        String suggested = "droid@android.com";
+        int startIndex = text.indexOf(selected);
+        int endIndex = startIndex + selected.length();
+        int smartStartIndex = text.indexOf(suggested);
+        int smartEndIndex = smartStartIndex + suggested.length();
+        TextSelection.Request request = new TextSelection.Request.Builder(
+                text, startIndex, endIndex)
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextSelection selection = mClassifier.suggestSelection(request);
+        assertThat(selection,
+                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
+    }
+
+    @Test
+    public void testSmartSelection_url() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Visit http://www.android.com for more information";
+        String selected = "http";
+        String suggested = "http://www.android.com";
+        int startIndex = text.indexOf(selected);
+        int endIndex = startIndex + selected.length();
+        int smartStartIndex = text.indexOf(suggested);
+        int smartEndIndex = smartStartIndex + suggested.length();
+        TextSelection.Request request = new TextSelection.Request.Builder(
+                text, startIndex, endIndex)
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextSelection selection = mClassifier.suggestSelection(request);
+        assertThat(selection,
+                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
+    }
+
+    @Test
+    public void testSmartSelection_withEmoji() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "\uD83D\uDE02 Hello.";
+        String selected = "Hello";
+        int startIndex = text.indexOf(selected);
+        int endIndex = startIndex + selected.length();
+        TextSelection.Request request = new TextSelection.Request.Builder(
+                text, startIndex, endIndex)
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextSelection selection = mClassifier.suggestSelection(request);
+        assertThat(selection,
+                isTextSelection(startIndex, endIndex, NO_TYPE));
+    }
+
+    @Test
+    public void testClassifyText() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Contact me at droid@android.com";
+        String classifiedText = "droid@android.com";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+        TextClassification.Request request = new TextClassification.Request.Builder(
+                text, startIndex, endIndex)
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextClassification classification = mClassifier.classifyText(request);
+        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_EMAIL));
+    }
+
+    @Test
+    public void testTextClassifyText_url() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Visit www.android.com for more information";
+        String classifiedText = "www.android.com";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+        TextClassification.Request request = new TextClassification.Request.Builder(
+                text, startIndex, endIndex)
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextClassification classification = mClassifier.classifyText(request);
+        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
+    }
+
+    @Test
+    public void testTextClassifyText_address() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Brandschenkestrasse 110, Zürich, Switzerland";
+        TextClassification.Request request = new TextClassification.Request.Builder(
+                text, 0, text.length())
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextClassification classification = mClassifier.classifyText(request);
+        assertThat(classification, isTextClassification(text, TextClassifier.TYPE_ADDRESS));
+    }
+
+    @Test
+    public void testTextClassifyText_url_inCaps() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Visit HTTP://ANDROID.COM for more information";
+        String classifiedText = "HTTP://ANDROID.COM";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+        TextClassification.Request request = new TextClassification.Request.Builder(
+                text, startIndex, endIndex)
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextClassification classification = mClassifier.classifyText(request);
+        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
+    }
+
+    @Test
+    public void testTextClassifyText_date() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Let's meet on January 9, 2018.";
+        String classifiedText = "January 9, 2018";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+        TextClassification.Request request = new TextClassification.Request.Builder(
+                text, startIndex, endIndex)
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextClassification classification = mClassifier.classifyText(request);
+        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_DATE));
+    }
+
+    @Test
+    public void testTextClassifyText_datetime() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Let's meet 2018/01/01 10:30:20.";
+        String classifiedText = "2018/01/01 10:30:20";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+        TextClassification.Request request = new TextClassification.Request.Builder(
+                text, startIndex, endIndex)
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextClassification classification = mClassifier.classifyText(request);
+        assertThat(classification,
+                isTextClassification(classifiedText, TextClassifier.TYPE_DATE_TIME));
+    }
+
+    @Test
+    public void testGenerateLinks_phone() {
+        if (isTextClassifierDisabled()) return;
+        String text = "The number is +12122537077. See you tonight!";
+        TextLinks.Request request = new TextLinks.Request.Builder(text).build();
+        assertThat(mClassifier.generateLinks(request),
+                isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE));
+    }
+
+    @Test
+    public void testGenerateLinks_exclude() {
+        if (isTextClassifierDisabled()) return;
+        String text = "You want apple@banana.com. See you tonight!";
+        List<String> hints = Collections.EMPTY_LIST;
+        List<String> included = Collections.EMPTY_LIST;
+        List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
+        TextLinks.Request request = new TextLinks.Request.Builder(text)
+                .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
+                .setDefaultLocales(LOCALES)
+                .build();
+        assertThat(mClassifier.generateLinks(request),
+                not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
+    }
+
+    @Test
+    public void testGenerateLinks_explicit_address() {
+        if (isTextClassifierDisabled()) return;
+        String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
+        List<String> explicit = Arrays.asList(TextClassifier.TYPE_ADDRESS);
+        TextLinks.Request request = new TextLinks.Request.Builder(text)
+                .setEntityConfig(TextClassifier.EntityConfig.createWithExplicitEntityList(explicit))
+                .setDefaultLocales(LOCALES)
+                .build();
+        assertThat(mClassifier.generateLinks(request),
+                isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
+                        TextClassifier.TYPE_ADDRESS));
+    }
+
+    @Test
+    public void testGenerateLinks_exclude_override() {
+        if (isTextClassifierDisabled()) return;
+        String text = "You want apple@banana.com. See you tonight!";
+        List<String> hints = Collections.EMPTY_LIST;
+        List<String> included = Arrays.asList(TextClassifier.TYPE_EMAIL);
+        List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
+        TextLinks.Request request = new TextLinks.Request.Builder(text)
+                .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
+                .setDefaultLocales(LOCALES)
+                .build();
+        assertThat(mClassifier.generateLinks(request),
+                not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
+    }
+
+    @Test
+    public void testGenerateLinks_maxLength() {
+        if (isTextClassifierDisabled()) return;
+        char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength()];
+        Arrays.fill(manySpaces, ' ');
+        TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
+        TextLinks links = mClassifier.generateLinks(request);
+        assertTrue(links.getLinks().isEmpty());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGenerateLinks_tooLong() {
+        if (isTextClassifierDisabled()) {
+            throw new IllegalArgumentException("pass if disabled");
+        }
+        char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
+        Arrays.fill(manySpaces, ' ');
+        TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
+        mClassifier.generateLinks(request);
+    }
+
+    @Test
+    public void testDetectLanguage() {
+        if (isTextClassifierDisabled()) return;
+        String text = "This is English text";
+        TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
+        TextLanguage textLanguage = mClassifier.detectLanguage(request);
+        assertThat(textLanguage, isTextLanguage("en"));
+    }
+
+    @Test
+    public void testDetectLanguage_japanese() {
+        if (isTextClassifierDisabled()) return;
+        String text = "これは日本語のテキストです";
+        TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
+        TextLanguage textLanguage = mClassifier.detectLanguage(request);
+        assertThat(textLanguage, isTextLanguage("ja"));
+    }
+
+    @Test
+    public void testSuggestConversationActions_textReplyOnly_maxThree() {
+        if (isTextClassifierDisabled()) return;
+        ConversationActions.Message message =
+                new ConversationActions.Message.Builder().setText("Hello").build();
+        ConversationActions.TypeConfig typeConfig =
+                new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
+                        .setIncludedTypes(
+                                Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+                        .build();
+        ConversationActions.Request request =
+                new ConversationActions.Request.Builder(Collections.singletonList(message))
+                        .setMaxSuggestions(3)
+                        .setTypeConfig(typeConfig)
+                        .build();
+
+        ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
+        assertTrue(conversationActions.getConversationActions().size() > 0);
+        assertTrue(conversationActions.getConversationActions().size() <= 3);
+        for (ConversationActions.ConversationAction conversationAction :
+                conversationActions.getConversationActions()) {
+            assertEquals(conversationAction.getType(), ConversationActions.TYPE_TEXT_REPLY);
+            assertNotNull(conversationAction.getTextReply());
+            assertTrue(conversationAction.getConfidenceScore() > 0);
+            assertTrue(conversationAction.getConfidenceScore() <= 1);
+        }
+    }
+
+
+    private boolean isTextClassifierDisabled() {
+        return mClassifier == null || mClassifier == TextClassifier.NO_OP;
+    }
+
+    private static Matcher<TextSelection> isTextSelection(
+            final int startIndex, final int endIndex, final String type) {
+        return new BaseMatcher<TextSelection>() {
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof TextSelection) {
+                    TextSelection selection = (TextSelection) o;
+                    return startIndex == selection.getSelectionStartIndex()
+                            && endIndex == selection.getSelectionEndIndex()
+                            && typeMatches(selection, type);
+                }
+                return false;
+            }
+
+            private boolean typeMatches(TextSelection selection, String type) {
+                return type == null
+                        || (selection.getEntityCount() > 0
+                        && type.trim().equalsIgnoreCase(selection.getEntity(0)));
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendValue(
+                        String.format("%d, %d, %s", startIndex, endIndex, type));
+            }
+        };
+    }
+
+    private static Matcher<TextLinks> isTextLinksContaining(
+            final String text, final String substring, final String type) {
+        return new BaseMatcher<TextLinks>() {
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("text=").appendValue(text)
+                        .appendText(", substring=").appendValue(substring)
+                        .appendText(", type=").appendValue(type);
+            }
+
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof TextLinks) {
+                    for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) {
+                        if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) {
+                            return type.equals(link.getEntity(0));
+                        }
+                    }
+                }
+                return false;
+            }
+        };
+    }
+
+    private static Matcher<TextClassification> isTextClassification(
+            final String text, final String type) {
+        return new BaseMatcher<TextClassification>() {
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof TextClassification) {
+                    TextClassification result = (TextClassification) o;
+                    return text.equals(result.getText())
+                            && result.getEntityCount() > 0
+                            && type.equals(result.getEntity(0));
+                }
+                return false;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("text=").appendValue(text)
+                        .appendText(", type=").appendValue(type);
+            }
+        };
+    }
+
+    private static Matcher<TextLanguage> isTextLanguage(final String languageTag) {
+        return new BaseMatcher<TextLanguage>() {
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof TextLanguage) {
+                    TextLanguage result = (TextLanguage) o;
+                    return result.getLocaleHypothesisCount() > 0
+                            && languageTag.equals(result.getLocale(0).toLanguageTag());
+                }
+                return false;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("locale=").appendValue(languageTag);
+            }
+        };
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index c866bc4..b242a34 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -39,13 +39,16 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.function.Predicate;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KernelCpuThreadReaderTest {
 
-    private static final String PROCESS_NAME = "test_process";
+    private static final int UID = 1000;
+    private static final int PROCESS_ID = 1234;
     private static final int[] THREAD_IDS = {0, 1000, 1235, 4321};
+    private static final String PROCESS_NAME = "test_process";
     private static final String[] THREAD_NAMES = {
             "test_thread_1", "test_thread_2", "test_thread_3", "test_thread_4"
     };
@@ -73,49 +76,126 @@
     }
 
     @Test
-    public void testSimple() throws IOException {
-        // Make /proc/self
-        final Path selfPath = mProcDirectory.toPath().resolve("self");
-        assertTrue(selfPath.toFile().mkdirs());
+    public void testReader_currentProcess() throws IOException {
+        KernelCpuThreadReader.Injector processUtils =
+                new KernelCpuThreadReader.Injector() {
+                    @Override
+                    public int myPid() {
+                        return PROCESS_ID;
+                    }
 
-        // Make /proc/self/task
-        final Path selfThreadsPath = selfPath.resolve("task");
+                    @Override
+                    public int myUid() {
+                        return UID;
+                    }
+
+                    @Override
+                    public int getUidForPid(int pid) {
+                        return 0;
+                    }
+                };
+        setupDirectory(mProcDirectory.toPath().resolve("self"), THREAD_IDS, PROCESS_NAME,
+                THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
+
+        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                mProcDirectory.toPath(),
+                mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"),
+                processUtils);
+        final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+                kernelCpuThreadReader.getCurrentProcessCpuUsage();
+        checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(), UID, PROCESS_ID,
+                THREAD_IDS, PROCESS_NAME, THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
+    }
+
+    @Test
+    public void testReader_byUids() throws IOException {
+        int[] uids = new int[]{0, 2, 3, 4, 5, 6000};
+        Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4;
+        int[] expectedUids = new int[]{0, 4, 5, 6000};
+        KernelCpuThreadReader.Injector processUtils =
+                new KernelCpuThreadReader.Injector() {
+                    @Override
+                    public int myPid() {
+                        return 0;
+                    }
+
+                    @Override
+                    public int myUid() {
+                        return 0;
+                    }
+
+                    @Override
+                    public int getUidForPid(int pid) {
+                        return pid;
+                    }
+                };
+
+        for (int uid : uids) {
+            setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)),
+                    new int[]{uid * 10},
+                    "process" + uid, new String[]{"thread" + uid}, new int[]{1000},
+                    new int[][]{{uid}});
+        }
+        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                mProcDirectory.toPath(),
+                mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
+                processUtils);
+        ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
+                kernelCpuThreadReader.getProcessCpuUsageByUids(uidPredicate);
+        processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.processId));
+
+        assertEquals(expectedUids.length, processCpuUsageByUids.size());
+        for (int i = 0; i < expectedUids.length; i++) {
+            KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+                    processCpuUsageByUids.get(i);
+            int uid = expectedUids[i];
+            checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(),
+                    uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid},
+                    new int[]{1000}, new int[][]{{uid}});
+        }
+    }
+
+    private void setupDirectory(Path processPath, int[] threadIds, String processName,
+            String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) throws IOException {
+        // Make /proc/$PID
+        assertTrue(processPath.toFile().mkdirs());
+
+        // Make /proc/$PID/task
+        final Path selfThreadsPath = processPath.resolve("task");
         assertTrue(selfThreadsPath.toFile().mkdirs());
 
-        // Make /proc/self/cmdline
-        Files.write(selfPath.resolve("cmdline"), PROCESS_NAME.getBytes());
+        // Make /proc/$PID/cmdline
+        Files.write(processPath.resolve("cmdline"), processName.getBytes());
 
         // Make thread directories in reverse order, as they are read in order of creation by
         // CpuThreadProcReader
-        for (int i = 0; i < THREAD_IDS.length; i++) {
-            // Make /proc/self/task/$TID
-            final Path threadPath = selfThreadsPath.resolve(String.valueOf(THREAD_IDS[i]));
+        for (int i = 0; i < threadIds.length; i++) {
+            // Make /proc/$PID/task/$TID
+            final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
             assertTrue(threadPath.toFile().mkdirs());
 
-            // Make /proc/self/task/$TID/comm
-            Files.write(threadPath.resolve("comm"), THREAD_NAMES[i].getBytes());
+            // Make /proc/$PID/task/$TID/comm
+            Files.write(threadPath.resolve("comm"), threadNames[i].getBytes());
 
-            // Make /proc/self/task/$TID/time_in_state
+            // Make /proc/$PID/task/$TID/time_in_state
             final OutputStream timeInStateStream =
                     Files.newOutputStream(threadPath.resolve("time_in_state"));
-            for (int j = 0; j < THREAD_CPU_FREQUENCIES.length; j++) {
-                final String line = String.valueOf(THREAD_CPU_FREQUENCIES[j]) + " "
-                        + String.valueOf(THREAD_CPU_TIMES[i][j]) + "\n";
+            for (int j = 0; j < cpuFrequencies.length; j++) {
+                final String line = String.valueOf(cpuFrequencies[j]) + " "
+                        + String.valueOf(cpuTimes[i][j]) + "\n";
                 timeInStateStream.write(line.getBytes());
             }
             timeInStateStream.close();
         }
+    }
 
-        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
-                mProcDirectory.toPath(),
-                mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"));
-        final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
-                kernelCpuThreadReader.getCurrentProcessCpuUsage();
-
+    private void checkResults(KernelCpuThreadReader.ProcessCpuUsage processCpuUsage,
+            int[] readerCpuFrequencies, int uid, int processId, int[] threadIds, String processName,
+            String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) {
         assertNotNull(processCpuUsage);
-        assertEquals(android.os.Process.myPid(), processCpuUsage.processId);
-        assertEquals(android.os.Process.myUid(), processCpuUsage.uid);
-        assertEquals(PROCESS_NAME, processCpuUsage.processName);
+        assertEquals(processId, processCpuUsage.processId);
+        assertEquals(uid, processCpuUsage.uid);
+        assertEquals(processName, processCpuUsage.processName);
 
         // Sort the thread CPU usages to compare with test case
         final ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
@@ -124,21 +204,21 @@
 
         int threadCount = 0;
         for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage : threadCpuUsages) {
-            assertEquals(THREAD_IDS[threadCount], threadCpuUsage.threadId);
-            assertEquals(THREAD_NAMES[threadCount], threadCpuUsage.threadName);
+            assertEquals(threadIds[threadCount], threadCpuUsage.threadId);
+            assertEquals(threadNames[threadCount], threadCpuUsage.threadName);
 
             for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) {
                 assertEquals(
-                        THREAD_CPU_TIMES[threadCount][i] * 10,
+                        cpuTimes[threadCount][i] * 10,
                         threadCpuUsage.usageTimesMillis[i]);
                 assertEquals(
-                        THREAD_CPU_FREQUENCIES[i],
-                        kernelCpuThreadReader.getCpuFrequenciesKhz()[i]);
+                        cpuFrequencies[i],
+                        readerCpuFrequencies[i]);
             }
             threadCount++;
         }
 
-        assertEquals(threadCount, THREAD_IDS.length);
+        assertEquals(threadCount, threadIds.length);
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
index f2a531f..2893066 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
@@ -70,6 +70,23 @@
     }
 
     @Test
+    public void testHeaderFormat() throws IOException {
+        final Path initialTimeInStateFile = mProcDirectory.toPath().resolve(
+                "initial-time-in-state");
+        Files.write(initialTimeInStateFile, "header1\n1 2\nheader2:\n3 4\n5 6\n7 8\n".getBytes());
+        final ProcTimeInStateReader reader = new ProcTimeInStateReader(initialTimeInStateFile);
+
+        assertArrayEquals(
+                "Reported frequencies are correct",
+                new long[]{1, 3, 5, 7},
+                reader.getFrequenciesKhz());
+        assertArrayEquals(
+                "Reported usage times are correct",
+                new long[]{20, 40, 60, 80},
+                reader.getUsageTimesMillis(initialTimeInStateFile));
+    }
+
+    @Test
     public void testDifferentFile() throws IOException {
         Path initialTimeInStateFile = mProcDirectory.toPath().resolve("initial-time-in-state");
         Files.write(initialTimeInStateFile, "1 2\n3 4\n5 6\n7 8\n".getBytes());
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 7216a22..072fe73 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -282,8 +282,11 @@
      * Returns {@code true} if the entry no longer exists.
      */
     public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
-        return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid) ||
-                keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
+        int ret = keystore.delete2(Credentials.USER_PRIVATE_KEY + alias, uid);
+        if (ret == KeyStore.KEY_NOT_FOUND) {
+            return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
+        }
+        return ret == KeyStore.NO_ERROR;
     }
 
     /**
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 6d58d95..6e6ed30 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -267,16 +267,20 @@
         }
     }
 
-    public boolean delete(String key, int uid) {
+    int delete2(String key, int uid) {
         try {
-            int ret = mBinder.del(key, uid);
-            return (ret == NO_ERROR || ret == KEY_NOT_FOUND);
+            return mBinder.del(key, uid);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
-            return false;
+            return SYSTEM_ERROR;
         }
     }
 
+    public boolean delete(String key, int uid) {
+        int ret = delete2(key, uid);
+        return ret == NO_ERROR || ret == KEY_NOT_FOUND;
+    }
+
     @UnsupportedAppUsage
     public boolean delete(String key) {
         return delete(key, UID_SELF);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9c707bab..9a15ff2 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -30,6 +30,7 @@
 #include <SkColorSpaceXformCanvas.h>
 #include <SkDeque.h>
 #include <SkDrawable.h>
+#include <SkFont.h>
 #include <SkGraphics.h>
 #include <SkImage.h>
 #include <SkImagePriv.h>
@@ -732,6 +733,7 @@
                             float y, float boundsLeft, float boundsTop, float boundsRight,
                             float boundsBottom, float totalAdvance) {
     if (count <= 0 || paint.nothingToDraw()) return;
+    SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
     SkPaint paintCopy(paint);
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
@@ -748,7 +750,7 @@
             SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, boundsRight + x, boundsBottom + y);
 
     SkTextBlobBuilder builder;
-    const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(paintCopy, count, &bounds);
+    const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(font, count, &bounds);
     glyphFunc(buffer.glyphs, buffer.pos);
 
     sk_sp<SkTextBlob> textBlob(builder.make());
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index c5a6ec5..f1d9397 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -28,10 +28,13 @@
         "libgui",
         "libui",
         "libinput",
-        "libinputflinger",
         "libnativewindow",
     ],
 
+    header_libs: [
+        "libinputflinger_headers",
+    ],
+
     include_dirs: ["frameworks/native/services"],
 
     cflags: [
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index eb3469e..7f4e5a5 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -24,7 +24,7 @@
 
 #include <ui/DisplayInfo.h>
 #include <input/Input.h>
-#include <inputflinger/PointerControllerInterface.h>
+#include <PointerControllerInterface.h>
 #include <utils/BitSet.h>
 #include <utils/RefBase.h>
 #include <utils/Looper.h>
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index f1325ce..4f23cca 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -595,10 +595,10 @@
                 case CONTENT_TYPE_MUSIC:
                 case CONTENT_TYPE_SONIFICATION:
                 case CONTENT_TYPE_SPEECH:
-                     mContentType = contentType;
-                     break;
+                    mContentType = contentType;
+                    break;
                 default:
-                     mUsage = CONTENT_TYPE_UNKNOWN;
+                    mContentType = CONTENT_TYPE_UNKNOWN;
             }
             return this;
         }
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
deleted file mode 100644
index a633e5f..0000000
--- a/media/java/android/media/MediaController2.java
+++ /dev/null
@@ -1,863 +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.MediaPlayerBase.BUFFERING_STATE_UNKNOWN;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.media.MediaPlaylistAgent.RepeatMode;
-import android.media.MediaPlaylistAgent.ShuffleMode;
-import android.media.MediaSession2.CommandButton;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.ErrorCode;
-import android.media.session.MediaSessionManager;
-import android.media.update.ApiLoader;
-import android.media.update.MediaController2Provider;
-import android.media.update.MediaController2Provider.PlaybackInfoProvider;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- * Allows an app to interact with an active {@link MediaSession2} in any status. Media buttons and
- * other commands can be sent to the session.
- * <p>
- * When you're done, use {@link #close()} to clean up resources. This also helps session service
- * to be destroyed when there's no controller associated with it.
- * <p>
- * When controlling {@link MediaSession2}, the controller will be available immediately after
- * the creation.
- * <p>
- * A controller can be created through token from {@link MediaSessionManager} if you hold the
- * signature|privileged permission "android.permission.MEDIA_CONTENT_CONTROL" permission or are
- * an enabled notification listener or by getting a {@link SessionToken2} directly the
- * the session owner.
- * <p>
- * MediaController2 objects are thread-safe.
- * <p>
- * @see MediaSession2
- */
-public class MediaController2 implements AutoCloseable {
-    /**
-     * Interface for listening to change in activeness of the {@link MediaSession2}.  It's
-     * active if and only if it has set a player.
-     */
-    public abstract static class ControllerCallback {
-        /**
-         * Called when the controller is successfully connected to the session. The controller
-         * becomes available afterwards.
-         *
-         * @param controller the controller for this event
-         * @param allowedCommands commands that's allowed by the session.
-         */
-        public void onConnected(@NonNull MediaController2 controller,
-                @NonNull SessionCommandGroup2 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
-         * @param controller controller for this event
-         */
-        public void onDisconnected(@NonNull MediaController2 controller) { }
-
-        /**
-         * Called when the session set the custom layout through the
-         * {@link MediaSession2#setCustomLayout(ControllerInfo, List)}.
-         * <p>
-         * Can be called before {@link #onConnected(MediaController2, SessionCommandGroup2)} is
-         * called.
-         *
-         * @param controller the controller for this event
-         * @param layout
-         */
-        public void onCustomLayoutChanged(@NonNull MediaController2 controller,
-                @NonNull List<CommandButton> layout) { }
-
-        /**
-         * Called when the session has changed anything related with the {@link PlaybackInfo}.
-         *
-         * @param controller the controller for this event
-         * @param info new playback info
-         */
-        public void onPlaybackInfoChanged(@NonNull MediaController2 controller,
-                @NonNull PlaybackInfo info) { }
-
-        /**
-         * Called when the allowed commands are changed by session.
-         *
-         * @param controller the controller for this event
-         * @param commands newly allowed commands
-         */
-        public void onAllowedCommandsChanged(@NonNull MediaController2 controller,
-                @NonNull SessionCommandGroup2 commands) { }
-
-        /**
-         * Called when the session sent a custom command.
-         *
-         * @param controller the controller for this event
-         * @param command
-         * @param args
-         * @param receiver
-         */
-        public void onCustomCommand(@NonNull MediaController2 controller,
-                @NonNull SessionCommand2 command, @Nullable Bundle args,
-                @Nullable ResultReceiver receiver) { }
-
-        /**
-         * Called when the player state is changed.
-         *
-         * @param controller the controller for this event
-         * @param state
-         */
-        public void onPlayerStateChanged(@NonNull MediaController2 controller, int state) { }
-
-        /**
-         * Called when playback speed is changed.
-         *
-         * @param controller the controller for this event
-         * @param speed speed
-         */
-        public void onPlaybackSpeedChanged(@NonNull MediaController2 controller,
-                float speed) { }
-
-        /**
-         * Called to report buffering events for a data source.
-         * <p>
-         * Use {@link #getBufferedPosition()} for current buffering position.
-         *
-         * @param controller the controller for this event
-         * @param item the media item for which buffering is happening.
-         * @param state the new buffering state.
-         */
-        public void onBufferingStateChanged(@NonNull MediaController2 controller,
-                @NonNull MediaItem2 item, @MediaPlayerBase.BuffState int state) { }
-
-        /**
-         * Called to indicate that seeking is completed.
-         *
-         * @param controller the controller for this event.
-         * @param position the previous seeking request.
-         */
-        public void onSeekCompleted(@NonNull MediaController2 controller, long position) { }
-
-        /**
-         * Called when a error from
-         *
-         * @param controller the controller for this event
-         * @param errorCode error code
-         * @param extras extra information
-         */
-        public void onError(@NonNull MediaController2 controller, @ErrorCode int errorCode,
-                @Nullable Bundle extras) { }
-
-        /**
-         * Called when the player's currently playing item is changed
-         * <p>
-         * When it's called, you should invalidate previous playback information and wait for later
-         * callbacks.
-         *
-         * @param controller the controller for this event
-         * @param item new item
-         * @see #onBufferingStateChanged(MediaController2, MediaItem2, int)
-         */
-        // TODO(jaewan): Use this (b/74316764)
-        public void onCurrentMediaItemChanged(@NonNull MediaController2 controller,
-                @NonNull MediaItem2 item) { }
-
-        /**
-         * Called when a playlist is changed.
-         *
-         * @param controller the controller for this event
-         * @param list new playlist
-         * @param metadata new metadata
-         */
-        public void onPlaylistChanged(@NonNull MediaController2 controller,
-                @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) { }
-
-        /**
-         * Called when a playlist metadata is changed.
-         *
-         * @param controller the controller for this event
-         * @param metadata new metadata
-         */
-        public void onPlaylistMetadataChanged(@NonNull MediaController2 controller,
-                @Nullable MediaMetadata2 metadata) { }
-
-        /**
-         * Called when the shuffle mode is changed.
-         *
-         * @param controller the controller for this event
-         * @param shuffleMode repeat mode
-         * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
-         * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
-         * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
-         */
-        public void onShuffleModeChanged(@NonNull MediaController2 controller,
-                @MediaPlaylistAgent.ShuffleMode int shuffleMode) { }
-
-        /**
-         * Called when the repeat mode is changed.
-         *
-         * @param controller the controller for this event
-         * @param repeatMode repeat mode
-         * @see MediaPlaylistAgent#REPEAT_MODE_NONE
-         * @see MediaPlaylistAgent#REPEAT_MODE_ONE
-         * @see MediaPlaylistAgent#REPEAT_MODE_ALL
-         * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
-         */
-        public void onRepeatModeChanged(@NonNull MediaController2 controller,
-                @MediaPlaylistAgent.RepeatMode int repeatMode) { }
-    }
-
-    /**
-     * Holds information about the current playback and how audio is handled for
-     * this session.
-     */
-    // The same as MediaController.PlaybackInfo
-    public static final class PlaybackInfo {
-        /**
-         * The session uses remote playback.
-         */
-        public static final int PLAYBACK_TYPE_REMOTE = 2;
-        /**
-         * The session uses local playback.
-         */
-        public static final int PLAYBACK_TYPE_LOCAL = 1;
-
-        private final PlaybackInfoProvider mProvider;
-
-        /**
-         * @hide
-         */
-        public PlaybackInfo(PlaybackInfoProvider provider) {
-            mProvider = provider;
-        }
-
-        /**
-         * @hide
-         */
-        public PlaybackInfoProvider getProvider() {
-            return mProvider;
-        }
-
-        /**
-         * Get the type of playback which affects volume handling. One of:
-         * <ul>
-         * <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
-         * <li>{@link #PLAYBACK_TYPE_REMOTE}</li>
-         * </ul>
-         *
-         * @return The type of playback this session is using.
-         */
-        public int getPlaybackType() {
-            return mProvider.getPlaybackType_impl();
-        }
-
-        /**
-         * Get the audio attributes for this session. The attributes will affect
-         * volume handling for the session. When the volume type is
-         * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
-         * remote volume handler.
-         *
-         * @return The attributes for this session.
-         */
-        public AudioAttributes getAudioAttributes() {
-            return mProvider.getAudioAttributes_impl();
-        }
-
-        /**
-         * Get the type of volume control that can be used. One of:
-         * <ul>
-         * <li>{@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}</li>
-         * <li>{@link VolumeProvider2#VOLUME_CONTROL_RELATIVE}</li>
-         * <li>{@link VolumeProvider2#VOLUME_CONTROL_FIXED}</li>
-         * </ul>
-         *
-         * @return The type of volume control that may be used with this session.
-         */
-        public int getControlType() {
-            return mProvider.getControlType_impl();
-        }
-
-        /**
-         * Get the maximum volume that may be set for this session.
-         *
-         * @return The maximum allowed volume where this session is playing.
-         */
-        public int getMaxVolume() {
-            return mProvider.getMaxVolume_impl();
-        }
-
-        /**
-         * Get the current volume for this session.
-         *
-         * @return The current volume where this session is playing.
-         */
-        public int getCurrentVolume() {
-            return mProvider.getCurrentVolume_impl();
-        }
-    }
-
-    private final MediaController2Provider mProvider;
-
-    /**
-     * Create a {@link MediaController2} from the {@link SessionToken2}.
-     * 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 executor executor to run callbacks on.
-     * @param callback controller callback to receive changes in
-     */
-    public MediaController2(@NonNull Context context, @NonNull SessionToken2 token,
-            @NonNull @CallbackExecutor Executor executor, @NonNull ControllerCallback callback) {
-        super();
-
-        mProvider = createProvider(context, token, executor, callback);
-        // This also connects to the token.
-        // Explicit connect() isn't added on purpose because retrying connect() is impossible with
-        // session whose session binder is only valid while it's active.
-        // prevent a controller from reusable after the
-        // session is released and recreated.
-        mProvider.initialize();
-    }
-
-    MediaController2Provider createProvider(@NonNull Context context,
-            @NonNull SessionToken2 token, @NonNull Executor executor,
-            @NonNull ControllerCallback callback) {
-        return ApiLoader.getProvider().createMediaController2(
-                context, this, token, executor, callback);
-    }
-
-    /**
-     * Release this object, and disconnect from the session. After this, callbacks wouldn't be
-     * received.
-     */
-    @Override
-    public void close() {
-        mProvider.close_impl();
-    }
-
-    /**
-     * @hide
-     */
-    public MediaController2Provider getProvider() {
-        return mProvider;
-    }
-
-    /**
-     * @return token
-     */
-    public @NonNull SessionToken2 getSessionToken() {
-        return mProvider.getSessionToken_impl();
-    }
-
-    /**
-     * Returns whether this class is connected to active {@link MediaSession2} or not.
-     */
-    public boolean isConnected() {
-        return mProvider.isConnected_impl();
-    }
-
-    public void play() {
-        mProvider.play_impl();
-    }
-
-    public void pause() {
-        mProvider.pause_impl();
-    }
-
-    public void stop() {
-        mProvider.stop_impl();
-    }
-
-    /**
-     * Request that the player prepare its playback. In other words, other sessions can continue
-     * to play during the preparation of this session. This method can be used to speed up the
-     * start of the playback. Once the preparation is done, the session will change its playback
-     * state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards, {@link #play} can be called
-     * to start playback.
-     */
-    public void prepare() {
-        mProvider.prepare_impl();
-    }
-
-    /**
-     * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
-     */
-    public void fastForward() {
-        mProvider.fastForward_impl();
-    }
-
-    /**
-     * Rewinds playback. If playback is already rewinding this may increase the rate.
-     */
-    public void rewind() {
-        mProvider.rewind_impl();
-    }
-
-    /**
-     * Move to a new location in the media stream.
-     *
-     * @param pos Position to move to, in milliseconds.
-     */
-    public void seekTo(long pos) {
-        mProvider.seekTo_impl(pos);
-    }
-
-    /**
-     * Revisit this API later.
-     * @hide
-     */
-    public void skipForward() {
-        // TODO(jaewan): (Post-P) Discuss this API later.
-        // To match with KEYCODE_MEDIA_SKIP_FORWARD
-    }
-
-    /**
-     * @hide
-     */
-    public void skipBackward() {
-        // TODO(jaewan): (Post-P) Discuss this API later.
-        // To match with KEYCODE_MEDIA_SKIP_BACKWARD
-    }
-
-    /**
-     * Request that the player start playback for a specific media id.
-     *
-     * @param mediaId The id of the requested media.
-     * @param extras Optional extras that can include extra information about the media item
-     *               to be played.
-     */
-    public void playFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
-        mProvider.playFromMediaId_impl(mediaId, extras);
-    }
-
-    /**
-     * Request that the player start playback for a specific search query.
-     *
-     * @param query The search query. Should not be an empty string.
-     * @param extras Optional extras that can include extra information about the query.
-     */
-    public void playFromSearch(@NonNull String query, @Nullable Bundle extras) {
-        mProvider.playFromSearch_impl(query, extras);
-    }
-
-    /**
-     * Request that the player start playback for a specific {@link Uri}.
-     *
-     * @param uri The URI of the requested media.
-     * @param extras Optional extras that can include extra information about the media item
-     *               to be played.
-     */
-    public void playFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
-        mProvider.playFromUri_impl(uri, extras);
-    }
-
-    /**
-     * Request that the player prepare playback for a specific media id. In other words, other
-     * sessions can continue to play during the preparation of this session. This method can be
-     * used to speed up the start of the playback. Once the preparation is done, the session
-     * will change its playback state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards,
-     * {@link #play} can be called to start playback. If the preparation is not needed,
-     * {@link #playFromMediaId} can be directly called without this method.
-     *
-     * @param mediaId The id of the requested media.
-     * @param extras Optional extras that can include extra information about the media item
-     *               to be prepared.
-     */
-    public void prepareFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
-        mProvider.prepareFromMediaId_impl(mediaId, extras);
-    }
-
-    /**
-     * Request that the player prepare playback for a specific search query.
-     * In other words, other sessions can continue to play during the preparation of this session.
-     * This method can be used to speed up the start of the playback.
-     * Once the preparation is done, the session will change its playback state to
-     * {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards,
-     * {@link #play} can be called to start playback. If the preparation is not needed,
-     * {@link #playFromSearch} can be directly called without this method.
-     *
-     * @param query The search query. Should not be an empty string.
-     * @param extras Optional extras that can include extra information about the query.
-     */
-    public void prepareFromSearch(@NonNull String query, @Nullable Bundle extras) {
-        mProvider.prepareFromSearch_impl(query, extras);
-    }
-
-    /**
-     * Request that the player prepare playback for a specific {@link Uri}. In other words,
-     * other sessions can continue to play during the preparation of this session. This method
-     * can be used to speed up the start of the playback. Once the preparation is done, the
-     * session will change its playback state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}.
-     * Afterwards, {@link #play} can be called to start playback. If the preparation is not needed,
-     * {@link #playFromUri} can be directly called without this method.
-     *
-     * @param uri The URI of the requested media.
-     * @param extras Optional extras that can include extra information about the media item
-     *               to be prepared.
-     */
-    public void prepareFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
-        mProvider.prepareFromUri_impl(uri, extras);
-    }
-
-    /**
-     * Set the volume of the output this session is playing on. The command will be ignored if it
-     * does not support {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
-     * <p>
-     * If the session is local playback, this changes the device's volume with the stream that
-     * session's player is using. Flags will be specified for the {@link AudioManager}.
-     * <p>
-     * If the session is remote player (i.e. session has set volume provider), its volume provider
-     * will receive this request instead.
-     *
-     * @see #getPlaybackInfo()
-     * @param value The value to set it to, between 0 and the reported max.
-     * @param flags flags from {@link AudioManager} to include with the volume request for local
-     *              playback
-     */
-    public void setVolumeTo(int value, int flags) {
-        mProvider.setVolumeTo_impl(value, flags);
-    }
-
-    /**
-     * Adjust the volume of the output this session is playing on. The direction
-     * must be one of {@link AudioManager#ADJUST_LOWER},
-     * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
-     * The command will be ignored if the session does not support
-     * {@link VolumeProvider2#VOLUME_CONTROL_RELATIVE} or
-     * {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
-     * <p>
-     * If the session is local playback, this changes the device's volume with the stream that
-     * session's player is using. Flags will be specified for the {@link AudioManager}.
-     * <p>
-     * If the session is remote player (i.e. session has set volume provider), its volume provider
-     * will receive this request instead.
-     *
-     * @see #getPlaybackInfo()
-     * @param direction The direction to adjust the volume in.
-     * @param flags flags from {@link AudioManager} to include with the volume request for local
-     *              playback
-     */
-    public void adjustVolume(int direction, int flags) {
-        mProvider.adjustVolume_impl(direction, flags);
-    }
-
-    /**
-     * Get an intent for launching UI associated with this session if one exists.
-     *
-     * @return A {@link PendingIntent} to launch UI or null.
-     */
-    public @Nullable PendingIntent getSessionActivity() {
-        return mProvider.getSessionActivity_impl();
-    }
-
-    /**
-     * Get the lastly cached player state from
-     * {@link ControllerCallback#onPlayerStateChanged(MediaController2, int)}.
-     *
-     * @return player state
-     */
-    public int getPlayerState() {
-        return mProvider.getPlayerState_impl();
-    }
-
-    /**
-     * Gets the current playback position.
-     * <p>
-     * This returns the calculated value of the position, based on the difference between the
-     * update time and current time.
-     *
-     * @return position
-     */
-    public long getCurrentPosition() {
-        return mProvider.getCurrentPosition_impl();
-    }
-
-    /**
-     * Get the lastly cached playback speed from
-     * {@link ControllerCallback#onPlaybackSpeedChanged(MediaController2, float)}.
-     *
-     * @return speed
-     */
-    public float getPlaybackSpeed() {
-        return mProvider.getPlaybackSpeed_impl();
-    }
-
-    /**
-     * Set the playback speed.
-     */
-    public void setPlaybackSpeed(float speed) {
-        // TODO(jaewan): implement this (b/74093080)
-    }
-
-
-    /**
-     * Gets the current buffering state of the player.
-     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
-     * buffered.
-     * @return the buffering state.
-     */
-    public @MediaPlayerBase.BuffState int getBufferingState() {
-        // TODO(jaewan): Implement.
-        return BUFFERING_STATE_UNKNOWN;
-    }
-
-    /**
-     * Gets the lastly cached buffered position from the session when
-     * {@link ControllerCallback#onBufferingStateChanged(MediaController2, MediaItem2, int)} is
-     * called.
-     *
-     * @return buffering position in millis
-     */
-    public long getBufferedPosition() {
-        return mProvider.getBufferedPosition_impl();
-    }
-
-    /**
-     * Get the current playback info for this session.
-     *
-     * @return The current playback info or null.
-     */
-    public @Nullable PlaybackInfo getPlaybackInfo() {
-        return mProvider.getPlaybackInfo_impl();
-    }
-
-    /**
-     * Rate the media. This will cause the rating to be set for the current user.
-     * The rating style must follow the user rating style from the session.
-     * You can get the rating style from the session through the
-     * {@link MediaMetadata#getRating(String)} with the key
-     * {@link MediaMetadata#METADATA_KEY_USER_RATING}.
-     * <p>
-     * If the user rating was {@code null}, the media item does not accept setting user rating.
-     *
-     * @param mediaId The id of the media
-     * @param rating The rating to set
-     */
-    public void setRating(@NonNull String mediaId, @NonNull Rating2 rating) {
-        mProvider.setRating_impl(mediaId, rating);
-    }
-
-    /**
-     * Send custom command to the session
-     *
-     * @param command custom command
-     * @param args optional argument
-     * @param cb optional result receiver
-     */
-    public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
-            @Nullable ResultReceiver cb) {
-        mProvider.sendCustomCommand_impl(command, args, cb);
-    }
-
-    /**
-     * Returns the cached playlist from
-     * {@link ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)}.
-     * <p>
-     * This list may differ with the list that was specified with
-     * {@link #setPlaylist(List, MediaMetadata2)} depending on the session implementation. Use media
-     * items returned here for other playlist APIs such as {@link #skipToPlaylistItem(MediaItem2)}.
-     *
-     * @return The playlist. Can be {@code null} if the controller doesn't have enough permission or
-     *         the session hasn't set any playlist.
-     */
-    public @Nullable List<MediaItem2> getPlaylist() {
-        return mProvider.getPlaylist_impl();
-    }
-
-    /**
-     * Sets the playlist.
-     * <p>
-     * Even when the playlist is successfully set, use the playlist returned from
-     * {@link #getPlaylist()} for playlist APIs such as {@link #skipToPlaylistItem(MediaItem2)}.
-     * Otherwise the session in the remote process can't distinguish between media items.
-     *
-     * @param list playlist
-     * @param metadata metadata of the playlist
-     * @see #getPlaylist()
-     * @see ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)
-     */
-    public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
-        mProvider.setPlaylist_impl(list, metadata);
-    }
-
-    /**
-     * Updates the playlist metadata
-     *
-     * @param metadata metadata of the playlist
-     */
-    public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
-        mProvider.updatePlaylistMetadata_impl(metadata);
-    }
-
-    /**
-     * Gets the lastly cached playlist playlist metadata either from
-     * {@link ControllerCallback#onPlaylistMetadataChanged(MediaController2,  MediaMetadata2)} or
-     * {@link ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)}.
-     *
-     * @return metadata metadata of the playlist, or null if none is set
-     */
-    public @Nullable MediaMetadata2 getPlaylistMetadata() {
-        return mProvider.getPlaylistMetadata_impl();
-    }
-
-
-    /**
-     * Adds the media item to the playlist at position index. Index equals or greater than
-     * the current playlist size will add the item at the end of the playlist.
-     * <p>
-     * This will not change the currently playing media item.
-     * If index is less than or equal to the current index of the playlist,
-     * the current index of the playlist will be incremented correspondingly.
-     *
-     * @param index the index you want to add
-     * @param item the media item you want to add
-     */
-    public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
-        mProvider.addPlaylistItem_impl(index, item);
-    }
-
-    /**
-     * Removes the media item at index in the playlist.
-     *<p>
-     * If the item is the currently playing item of the playlist, current playback
-     * will be stopped and playback moves to next source in the list.
-     *
-     * @param item the media item you want to add
-     */
-    public void removePlaylistItem(@NonNull MediaItem2 item) {
-        mProvider.removePlaylistItem_impl(item);
-    }
-
-    /**
-     * Replace the media item at index in the playlist. This can be also used to update metadata of
-     * an item.
-     *
-     * @param index the index of the item to replace
-     * @param item the new item
-     */
-    public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
-        mProvider.replacePlaylistItem_impl(index, item);
-    }
-
-    /**
-     * Get the lastly cached current item from
-     * {@link ControllerCallback#onCurrentMediaItemChanged(MediaController2, MediaItem2)}.
-     *
-     * @return index of the current item
-     */
-    public MediaItem2 getCurrentMediaItem() {
-        return mProvider.getCurrentMediaItem_impl();
-    }
-
-    /**
-     * Skips to the previous item in the playlist.
-     * <p>
-     * This calls {@link MediaSession2#skipToPreviousItem()} if the session allows.
-     */
-     public void skipToPreviousItem() {
-         mProvider.skipToPreviousItem_impl();
-     }
-
-    /**
-     * Skips to the next item in the playlist.
-     * <p>
-     * This calls {@link MediaSession2#skipToNextItem()} if the session allows.
-     */
-    public void skipToNextItem() {
-        mProvider.skipToNextItem_impl();
-    }
-
-    /**
-     * Skips to the item in the playlist.
-     * <p>
-     * This calls {@link MediaSession2#skipToPlaylistItem(MediaItem2)} if the session allows.
-     *
-     * @param item The item in the playlist you want to play
-     */
-    public void skipToPlaylistItem(@NonNull MediaItem2 item) {
-        mProvider.skipToPlaylistItem_impl(item);
-    }
-
-    /**
-     * Gets the cached repeat mode from the {@link ControllerCallback#onRepeatModeChanged(
-     * MediaController2, int)}.
-     *
-     * @return repeat mode
-     * @see MediaPlaylistAgent#REPEAT_MODE_NONE
-     * @see MediaPlaylistAgent#REPEAT_MODE_ONE
-     * @see MediaPlaylistAgent#REPEAT_MODE_ALL
-     * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
-     */
-    public @RepeatMode int getRepeatMode() {
-        return mProvider.getRepeatMode_impl();
-    }
-
-    /**
-     * Sets the repeat mode.
-     *
-     * @param repeatMode repeat mode
-     * @see MediaPlaylistAgent#REPEAT_MODE_NONE
-     * @see MediaPlaylistAgent#REPEAT_MODE_ONE
-     * @see MediaPlaylistAgent#REPEAT_MODE_ALL
-     * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
-     */
-    public void setRepeatMode(@RepeatMode int repeatMode) {
-        mProvider.setRepeatMode_impl(repeatMode);
-    }
-
-    /**
-     * Gets the cached shuffle mode from the {@link ControllerCallback#onShuffleModeChanged(
-     * MediaController2, int)}.
-     *
-     * @return The shuffle mode
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
-     */
-    public @ShuffleMode int getShuffleMode() {
-        return mProvider.getShuffleMode_impl();
-    }
-
-    /**
-     * Sets the shuffle mode.
-     *
-     * @param shuffleMode The shuffle mode
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
-     */
-    public void setShuffleMode(@ShuffleMode int shuffleMode) {
-        mProvider.setShuffleMode_impl(shuffleMode);
-    }
-}
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
deleted file mode 100644
index 423a1fd..0000000
--- a/media/java/android/media/MediaItem2.java
+++ /dev/null
@@ -1,220 +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.media.update.ApiLoader;
-import android.media.update.MediaItem2Provider;
-import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * @hide
- * A class with information on a single media item with the metadata information.
- * Media item are application dependent so we cannot guarantee that they contain the right values.
- * <p>
- * When it's sent to a controller or browser, it's anonymized and data descriptor wouldn't be sent.
- * <p>
- * This object isn't a thread safe.
- */
-public class MediaItem2 {
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
-    public @interface Flags { }
-
-    /**
-     * Flag: Indicates that the item has children of its own.
-     */
-    public static final int FLAG_BROWSABLE = 1 << 0;
-
-    /**
-     * Flag: Indicates that the item is playable.
-     * <p>
-     * The id of this item may be passed to
-     * {@link MediaController2#playFromMediaId(String, Bundle)}
-     */
-    public static final int FLAG_PLAYABLE = 1 << 1;
-
-    private final MediaItem2Provider mProvider;
-
-    /**
-     * Create a new media item
-     * @hide
-     */
-    public MediaItem2(MediaItem2Provider provider) {
-        mProvider = provider;
-    }
-
-    /**
-     * @hide
-     */
-    public MediaItem2Provider getProvider() {
-        return mProvider;
-    }
-
-    /**
-     * Return this object as a bundle to share between processes.
-     *
-     * @return a new bundle instance
-     */
-    public Bundle toBundle() {
-        return mProvider.toBundle_impl();
-    }
-
-    public static MediaItem2 fromBundle(Bundle bundle) {
-        return ApiLoader.getProvider().fromBundle_MediaItem2(bundle);
-    }
-
-    public String toString() {
-        return mProvider.toString_impl();
-    }
-
-    /**
-     * Gets the flags of the item.
-     */
-    public @Flags int getFlags() {
-        return mProvider.getFlags_impl();
-    }
-
-    /**
-     * Returns whether this item is browsable.
-     * @see #FLAG_BROWSABLE
-     */
-    public boolean isBrowsable() {
-        return mProvider.isBrowsable_impl();
-    }
-
-    /**
-     * Returns whether this item is playable.
-     * @see #FLAG_PLAYABLE
-     */
-    public boolean isPlayable() {
-        return mProvider.isPlayable_impl();
-    }
-
-    /**
-     * Set a metadata. If the metadata is not null, its id should be matched with this instance's
-     * media id.
-     *
-     * @param metadata metadata to update
-     */
-    public void setMetadata(@Nullable MediaMetadata2 metadata) {
-        mProvider.setMetadata_impl(metadata);
-    }
-
-    /**
-     * Returns the metadata of the media.
-     */
-    public @Nullable MediaMetadata2 getMetadata() {
-        return mProvider.getMetadata_impl();
-    }
-
-    /**
-     * Returns the media id for this item.
-     */
-    public @NonNull String getMediaId() {
-        return mProvider.getMediaId_impl();
-    }
-
-    /**
-     * Return the {@link DataSourceDesc}
-     * <p>
-     * Can be {@code null} if the MediaItem2 came from another process and anonymized
-     *
-     * @return data source descriptor
-     */
-    public @Nullable DataSourceDesc getDataSourceDesc() {
-        return mProvider.getDataSourceDesc_impl();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return mProvider.equals_impl(obj);
-    }
-
-    /**
-     * Build {@link MediaItem2}
-     */
-    public static final class Builder {
-        private final MediaItem2Provider.BuilderProvider mProvider;
-
-        /**
-         * Constructor for {@link Builder}
-         *
-         * @param flags
-         */
-        public Builder(@Flags int flags) {
-            mProvider = ApiLoader.getProvider().createMediaItem2Builder(this, flags);
-        }
-
-        /**
-         * Set the media id of this instance. {@code null} for unset.
-         * <p>
-         * Media id is used to identify a media contents between session and controller.
-         * <p>
-         * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
-         * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
-         * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
-         * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
-         *
-         * @param mediaId media id
-         * @return this instance for chaining
-         */
-        public Builder setMediaId(@Nullable String mediaId) {
-            return mProvider.setMediaId_impl(mediaId);
-        }
-
-        /**
-         * Set the metadata of this instance. {@code null} for unset.
-         * <p>
-         * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
-         * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
-         * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
-         * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
-         *
-         * @param metadata metadata
-         * @return this instance for chaining
-         */
-        public Builder setMetadata(@Nullable MediaMetadata2 metadata) {
-            return mProvider.setMetadata_impl(metadata);
-        }
-
-        /**
-         * Set the data source descriptor for this instance. {@code null} for unset.
-         *
-         * @param dataSourceDesc data source descriptor
-         * @return this instance for chaining
-         */
-        public Builder setDataSourceDesc(@Nullable DataSourceDesc dataSourceDesc) {
-            return mProvider.setDataSourceDesc_impl(dataSourceDesc);
-        }
-
-        /**
-         * Build {@link MediaItem2}.
-         *
-         * @return a new {@link MediaItem2}.
-         */
-        public MediaItem2 build() {
-            return mProvider.build_impl();
-        }
-    }
-}
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
deleted file mode 100644
index 7b03ae0..0000000
--- a/media/java/android/media/MediaMetadata2.java
+++ /dev/null
@@ -1,854 +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.annotation.StringDef;
-import android.graphics.Bitmap;
-import android.media.update.ApiLoader;
-import android.media.update.MediaMetadata2Provider;
-import android.net.Uri;
-import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Set;
-
-/**
- * @hide
- * Contains metadata about an item, such as the title, artist, etc.
- */
-// New version of MediaMetadata with following changes
-//   - Don't implement Parcelable for updatable support.
-//   - Also support MediaDescription features. MediaDescription is deprecated instead because
-//     it was insufficient for controller to display media contents.
-public final class MediaMetadata2 {
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the title of the media.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the artist of the media.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
-
-    /**
-     * The metadata key for a {@link Long} typed value to retrieve the information about the
-     * duration of the media in ms. A negative duration indicates that the duration is unknown
-     * (or infinite).
-     *
-     * @see Builder#putLong(String, long)
-     * @see #getLong(String)
-     */
-    public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the album title for the media.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the author of the media.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the writer of the media.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the composer of the media.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the compilation status of the media.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the date the media was created or published.
-     * The format is unspecified but RFC 3339 is recommended.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
-
-    /**
-     * The metadata key for a {@link Long} typed value to retrieve the information about the year
-     * the media was created or published.
-     *
-     * @see Builder#putLong(String, long)
-     * @see #getLong(String)
-     */
-    public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the genre of the media.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
-
-    /**
-     * The metadata key for a {@link Long} typed value to retrieve the information about the
-     * track number for the media.
-     *
-     * @see Builder#putLong(String, long)
-     * @see #getLong(String)
-     */
-    public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
-
-    /**
-     * The metadata key for a {@link Long} typed value to retrieve the information about the
-     * number of tracks in the media's original source.
-     *
-     * @see Builder#putLong(String, long)
-     * @see #getLong(String)
-     */
-    public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
-
-    /**
-     * The metadata key for a {@link Long} typed value to retrieve the information about the
-     * disc number for the media's original source.
-     *
-     * @see Builder#putLong(String, long)
-     * @see #getLong(String)
-     */
-    public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the artist for the album of the media's original source.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
-
-    /**
-     * The metadata key for a {@link Bitmap} typed value to retrieve the information about the
-     * artwork for the media.
-     * The artwork should be relatively small and may be scaled down if it is too large.
-     * For higher resolution artwork, {@link #METADATA_KEY_ART_URI} should be used instead.
-     *
-     * @see Builder#putBitmap(String, Bitmap)
-     * @see #getBitmap(String)
-     */
-    public static final String METADATA_KEY_ART = "android.media.metadata.ART";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about Uri of the artwork for the media.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
-
-    /**
-     * The metadata key for a {@link Bitmap} typed value to retrieve the information about the
-     * artwork for the album of the media's original source.
-     * The artwork should be relatively small and may be scaled down if it is too large.
-     * For higher resolution artwork, {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
-     *
-     * @see Builder#putBitmap(String, Bitmap)
-     * @see #getBitmap(String)
-     */
-    public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the Uri of the artwork for the album of the media's original source.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
-
-    /**
-     * The metadata key for a {@link Rating2} typed value to retrieve the information about the
-     * user's rating for the media.
-     *
-     * @see Builder#putRating(String, Rating2)
-     * @see #getRating(String)
-     */
-    public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
-
-    /**
-     * The metadata key for a {@link Rating2} typed value to retrieve the information about the
-     * overall rating for the media.
-     *
-     * @see Builder#putRating(String, Rating2)
-     * @see #getRating(String)
-     */
-    public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the title that is suitable for display to the user.
-     * It will generally be the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
-     * When displaying media described by this metadata, this should be preferred if present.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the subtitle that is suitable for display to the user.
-     * When displaying a second line for media described by this metadata, this should be preferred
-     * to other fields if present.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_DISPLAY_SUBTITLE
-            = "android.media.metadata.DISPLAY_SUBTITLE";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the description that is suitable for display to the user.
-     * When displaying more information for media described by this metadata,
-     * this should be preferred to other fields if present.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_DISPLAY_DESCRIPTION
-            = "android.media.metadata.DISPLAY_DESCRIPTION";
-
-    /**
-     * The metadata key for a {@link Bitmap} typed value to retrieve the information about the icon
-     * or thumbnail that is suitable for display to the user.
-     * When displaying an icon for media described by this metadata, this should be preferred to
-     * other fields if present.
-     * <p>
-     * The icon should be relatively small and may be scaled down if it is too large.
-     * For higher resolution artwork, {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
-     *
-     * @see Builder#putBitmap(String, Bitmap)
-     * @see #getBitmap(String)
-     */
-    public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the Uri of icon or thumbnail that is suitable for display to the user.
-     * When displaying more information for media described by this metadata, the
-     * display description should be preferred to other fields when present.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_DISPLAY_ICON_URI
-            = "android.media.metadata.DISPLAY_ICON_URI";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the media ID of the content. This value is specific to the
-     * service providing the content. If used, this should be a persistent
-     * unique key for the underlying content.  It may be used with
-     * {@link MediaController2#playFromMediaId(String, Bundle)}
-     * to initiate playback when provided by a {@link MediaBrowser2} connected to
-     * the same app.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the Uri of the content. This value is specific to the service providing the
-     * content. It may be used with {@link MediaController2#playFromUri(Uri, Bundle)}
-     * to initiate playback when provided by a {@link MediaBrowser2} connected to the same app.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
-
-    /**
-     * The metadata key for a {@link Long} typed value to retrieve the information about the
-     * bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth
-     * AVRCP 1.5. It should be one of the following:
-     * <ul>
-     * <li>{@link #BT_FOLDER_TYPE_MIXED}</li>
-     * <li>{@link #BT_FOLDER_TYPE_TITLES}</li>
-     * <li>{@link #BT_FOLDER_TYPE_ALBUMS}</li>
-     * <li>{@link #BT_FOLDER_TYPE_ARTISTS}</li>
-     * <li>{@link #BT_FOLDER_TYPE_GENRES}</li>
-     * <li>{@link #BT_FOLDER_TYPE_PLAYLISTS}</li>
-     * <li>{@link #BT_FOLDER_TYPE_YEARS}</li>
-     * </ul>
-     *
-     * @see Builder#putLong(String, long)
-     * @see #getLong(String)
-     */
-    public static final String METADATA_KEY_BT_FOLDER_TYPE
-            = "android.media.metadata.BT_FOLDER_TYPE";
-
-    /**
-     * The type of folder that is unknown or contains media elements of mixed types as specified in
-     * the section 6.10.2.2 of the Bluetooth AVRCP 1.5.
-     */
-    public static final long BT_FOLDER_TYPE_MIXED = 0;
-
-    /**
-     * The type of folder that contains media elements only as specified in the section 6.10.2.2 of
-     * the Bluetooth AVRCP 1.5.
-     */
-    public static final long BT_FOLDER_TYPE_TITLES = 1;
-
-    /**
-     * The type of folder that contains folders categorized by album as specified in the section
-     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
-     */
-    public static final long BT_FOLDER_TYPE_ALBUMS = 2;
-
-    /**
-     * The type of folder that contains folders categorized by artist as specified in the section
-     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
-     */
-    public static final long BT_FOLDER_TYPE_ARTISTS = 3;
-
-    /**
-     * The type of folder that contains folders categorized by genre as specified in the section
-     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
-     */
-    public static final long BT_FOLDER_TYPE_GENRES = 4;
-
-    /**
-     * The type of folder that contains folders categorized by playlist as specified in the section
-     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
-     */
-    public static final long BT_FOLDER_TYPE_PLAYLISTS = 5;
-
-    /**
-     * The type of folder that contains folders categorized by year as specified in the section
-     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
-     */
-    public static final long BT_FOLDER_TYPE_YEARS = 6;
-
-    /**
-     * The metadata key for a {@link Long} typed value to retrieve the information about whether
-     * the media is an advertisement. A value of 0 indicates it is not an advertisement.
-     * A value of 1 or non-zero indicates it is an advertisement.
-     * If not specified, this value is set to 0 by default.
-     *
-     * @see Builder#putLong(String, long)
-     * @see #getLong(String)
-     */
-    public static final String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
-
-    /**
-     * The metadata key for a {@link Long} typed value to retrieve the information about the
-     * download status of the media which will be used for later offline playback. It should be
-     * one of the following:
-     *
-     * <ul>
-     * <li>{@link #STATUS_NOT_DOWNLOADED}</li>
-     * <li>{@link #STATUS_DOWNLOADING}</li>
-     * <li>{@link #STATUS_DOWNLOADED}</li>
-     * </ul>
-     *
-     * @see Builder#putLong(String, long)
-     * @see #getLong(String)
-     */
-    public static final String METADATA_KEY_DOWNLOAD_STATUS =
-            "android.media.metadata.DOWNLOAD_STATUS";
-
-    /**
-     * The status value to indicate the media item is not downloaded.
-     *
-     * @see #METADATA_KEY_DOWNLOAD_STATUS
-     */
-    public static final long STATUS_NOT_DOWNLOADED = 0;
-
-    /**
-     * The status value to indicate the media item is being downloaded.
-     *
-     * @see #METADATA_KEY_DOWNLOAD_STATUS
-     */
-    public static final long STATUS_DOWNLOADING = 1;
-
-    /**
-     * The status value to indicate the media item is downloaded for later offline playback.
-     *
-     * @see #METADATA_KEY_DOWNLOAD_STATUS
-     */
-    public static final long STATUS_DOWNLOADED = 2;
-
-    /**
-     * A {@link Bundle} extra.
-     */
-    public static final String METADATA_KEY_EXTRAS = "android.media.metadata.EXTRAS";
-
-    /**
-     * @hide
-     */
-    @StringDef({METADATA_KEY_TITLE, METADATA_KEY_ARTIST, METADATA_KEY_ALBUM, METADATA_KEY_AUTHOR,
-            METADATA_KEY_WRITER, METADATA_KEY_COMPOSER, METADATA_KEY_COMPILATION,
-            METADATA_KEY_DATE, METADATA_KEY_GENRE, METADATA_KEY_ALBUM_ARTIST, METADATA_KEY_ART_URI,
-            METADATA_KEY_ALBUM_ART_URI, METADATA_KEY_DISPLAY_TITLE, METADATA_KEY_DISPLAY_SUBTITLE,
-            METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_KEY_DISPLAY_ICON_URI,
-            METADATA_KEY_MEDIA_ID, METADATA_KEY_MEDIA_URI})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TextKey {}
-
-    /**
-     * @hide
-     */
-    @StringDef({METADATA_KEY_DURATION, METADATA_KEY_YEAR, METADATA_KEY_TRACK_NUMBER,
-            METADATA_KEY_NUM_TRACKS, METADATA_KEY_DISC_NUMBER, METADATA_KEY_BT_FOLDER_TYPE,
-            METADATA_KEY_ADVERTISEMENT, METADATA_KEY_DOWNLOAD_STATUS})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LongKey {}
-
-    /**
-     * @hide
-     */
-    @StringDef({METADATA_KEY_ART, METADATA_KEY_ALBUM_ART, METADATA_KEY_DISPLAY_ICON})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BitmapKey {}
-
-    /**
-     * @hide
-     */
-    @StringDef({METADATA_KEY_USER_RATING, METADATA_KEY_RATING})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RatingKey {}
-
-    /**
-     * @hide
-     */
-    // TODO(jaewan): Add predefined float key.
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FloatKey {}
-
-    private final MediaMetadata2Provider mProvider;
-
-    /**
-     * @hide
-     */
-    public MediaMetadata2(MediaMetadata2Provider provider) {
-        mProvider = provider;
-    }
-
-    /**
-     * Returns true if the given key is contained in the metadata
-     *
-     * @param key a String key
-     * @return true if the key exists in this metadata, false otherwise
-     */
-    public boolean containsKey(@NonNull String key) {
-        return mProvider.containsKey_impl(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if no mapping of
-     * the desired type exists for the given key or a null value is explicitly
-     * associated with the key.
-     *
-     * @param key The key the value is stored under
-     * @return a CharSequence value, or null
-     */
-    public @Nullable CharSequence getText(@NonNull @TextKey String key) {
-        return mProvider.getText_impl(key);
-    }
-
-    /**
-     * Returns the media id, or {@code null} if the id doesn't exist.
-     *<p>
-     * This is equivalent to the {@link #getString(String)} with the {@link #METADATA_KEY_MEDIA_ID}.
-     *
-     * @return media id. Can be {@code null}
-     * @see #METADATA_KEY_MEDIA_ID
-     */
-    public @Nullable String getMediaId() {
-        return mProvider.getMediaId_impl();
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if no mapping of
-     * the desired type exists for the given key or a null value is explicitly
-     * associated with the key.
-     *
-     * @param key The key the value is stored under
-     * @return a String value, or null
-     */
-    public @Nullable String getString(@NonNull @TextKey String key) {
-        return mProvider.getString_impl(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or 0L if no long exists
-     * for the given key.
-     *
-     * @param key The key the value is stored under
-     * @return a long value
-     */
-    public long getLong(@NonNull @LongKey String key) {
-        return mProvider.getLong_impl(key);
-    }
-
-    /**
-     * Return a {@link Rating2} for the given key or null if no rating exists for
-     * the given key.
-     * <p>
-     * For the {@link #METADATA_KEY_USER_RATING}, A {@code null} return value means that user rating
-     * cannot be set by {@link MediaController2}.
-     *
-     * @param key The key the value is stored under
-     * @return A {@link Rating2} or {@code null}
-     */
-    public @Nullable Rating2 getRating(@NonNull @RatingKey String key) {
-        return mProvider.getRating_impl(key);
-    }
-
-    /**
-     * Return a {@link Bitmap} for the given key or null if no bitmap exists for
-     * the given key.
-     *
-     * @param key The key the value is stored under
-     * @return A {@link Bitmap} or null
-     */
-    public @Nullable Bitmap getBitmap(@NonNull @BitmapKey String key) {
-        return mProvider.getBitmap_impl(key);
-    }
-
-    /**
-     * Return the value associated with the given key, or 0.0f if no long exists
-     * for the given key.
-     *
-     * @param key The key the value is stored under
-     * @return a float value
-     */
-    public float getFloat(@NonNull @FloatKey String key) {
-        return mProvider.getFloat_impl(key);
-    }
-
-    /**
-     * Get the extra {@link Bundle} from the metadata object.
-     *
-     * @return A {@link Bundle} or {@code null}
-     */
-    public @Nullable Bundle getExtras() {
-        return mProvider.getExtras_impl();
-    }
-
-    /**
-     * Get the number of fields in this metadata.
-     *
-     * @return The number of fields in the metadata.
-     */
-    public int size() {
-        return mProvider.size_impl();
-    }
-
-    /**
-     * Returns a Set containing the Strings used as keys in this metadata.
-     *
-     * @return a Set of String keys
-     */
-    public @NonNull Set<String> keySet() {
-        return mProvider.keySet_impl();
-    }
-
-    /**
-     * Gets the bundle backing the metadata object. This is available to support
-     * backwards compatibility. Apps should not modify the bundle directly.
-     *
-     * @return The Bundle backing this metadata.
-     */
-    public @NonNull Bundle toBundle() {
-        return mProvider.toBundle_impl();
-    }
-
-    /**
-     * Creates the {@link MediaMetadata2} from the bundle that previously returned by
-     * {@link #toBundle()}.
-     *
-     * @param bundle bundle for the metadata
-     * @return a new MediaMetadata2
-     */
-    public static @NonNull MediaMetadata2 fromBundle(@Nullable Bundle bundle) {
-        return ApiLoader.getProvider().fromBundle_MediaMetadata2(bundle);
-    }
-
-    /**
-     * Use to build MediaMetadata2 objects. The system defined metadata keys must
-     * use the appropriate data type.
-     */
-    public static final class Builder {
-        private final MediaMetadata2Provider.BuilderProvider mProvider;
-
-        /**
-         * Create an empty Builder. Any field that should be included in the
-         * {@link MediaMetadata2} must be added.
-         */
-        public Builder() {
-            mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(this);
-        }
-
-        /**
-         * Create a Builder using a {@link MediaMetadata2} instance to set the
-         * initial values. All fields in the source metadata will be included in
-         * the new metadata. Fields can be overwritten by adding the same key.
-         *
-         * @param source
-         */
-        public Builder(@NonNull MediaMetadata2 source) {
-            mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(this, source);
-        }
-
-        /**
-         * @hide
-         */
-        public Builder(@NonNull MediaMetadata2Provider.BuilderProvider provider) {
-            mProvider = provider;
-        }
-
-        /**
-         * Put a CharSequence value into the metadata. Custom keys may be used,
-         * but if the METADATA_KEYs defined in this class are used they may only
-         * be one of the following:
-         * <ul>
-         * <li>{@link #METADATA_KEY_TITLE}</li>
-         * <li>{@link #METADATA_KEY_ARTIST}</li>
-         * <li>{@link #METADATA_KEY_ALBUM}</li>
-         * <li>{@link #METADATA_KEY_AUTHOR}</li>
-         * <li>{@link #METADATA_KEY_WRITER}</li>
-         * <li>{@link #METADATA_KEY_COMPOSER}</li>
-         * <li>{@link #METADATA_KEY_DATE}</li>
-         * <li>{@link #METADATA_KEY_GENRE}</li>
-         * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
-         * <li>{@link #METADATA_KEY_ART_URI}</li>
-         * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
-         * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
-         * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
-         * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
-         * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
-         * </ul>
-         *
-         * @param key The key for referencing this value
-         * @param value The CharSequence value to store
-         * @return The Builder to allow chaining
-         */
-        public @NonNull Builder putText(@NonNull @TextKey String key,
-                @Nullable CharSequence value) {
-            return mProvider.putText_impl(key, value);
-        }
-
-        /**
-         * Put a String value into the metadata. Custom keys may be used, but if
-         * the METADATA_KEYs defined in this class are used they may only be one
-         * of the following:
-         * <ul>
-         * <li>{@link #METADATA_KEY_TITLE}</li>
-         * <li>{@link #METADATA_KEY_ARTIST}</li>
-         * <li>{@link #METADATA_KEY_ALBUM}</li>
-         * <li>{@link #METADATA_KEY_AUTHOR}</li>
-         * <li>{@link #METADATA_KEY_WRITER}</li>
-         * <li>{@link #METADATA_KEY_COMPOSER}</li>
-         * <li>{@link #METADATA_KEY_DATE}</li>
-         * <li>{@link #METADATA_KEY_GENRE}</li>
-         * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
-         * <li>{@link #METADATA_KEY_ART_URI}</li>
-         * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
-         * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
-         * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
-         * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
-         * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
-         * </ul>
-         *
-         * @param key The key for referencing this value
-         * @param value The String value to store
-         * @return The Builder to allow chaining
-         */
-        public @NonNull Builder putString(@NonNull @TextKey String key,
-                @Nullable String value) {
-            return mProvider.putString_impl(key, value);
-        }
-
-        /**
-         * Put a long value into the metadata. Custom keys may be used, but if
-         * the METADATA_KEYs defined in this class are used they may only be one
-         * of the following:
-         * <ul>
-         * <li>{@link #METADATA_KEY_DURATION}</li>
-         * <li>{@link #METADATA_KEY_TRACK_NUMBER}</li>
-         * <li>{@link #METADATA_KEY_NUM_TRACKS}</li>
-         * <li>{@link #METADATA_KEY_DISC_NUMBER}</li>
-         * <li>{@link #METADATA_KEY_YEAR}</li>
-         * <li>{@link #METADATA_KEY_BT_FOLDER_TYPE}</li>
-         * <li>{@link #METADATA_KEY_ADVERTISEMENT}</li>
-         * <li>{@link #METADATA_KEY_DOWNLOAD_STATUS}</li>
-         * </ul>
-         *
-         * @param key The key for referencing this value
-         * @param value The String value to store
-         * @return The Builder to allow chaining
-         */
-        public @NonNull Builder putLong(@NonNull @LongKey String key, long value) {
-            return mProvider.putLong_impl(key, value);
-        }
-
-        /**
-         * Put a {@link Rating2} into the metadata. Custom keys may be used, but
-         * if the METADATA_KEYs defined in this class are used they may only be
-         * one of the following:
-         * <ul>
-         * <li>{@link #METADATA_KEY_RATING}</li>
-         * <li>{@link #METADATA_KEY_USER_RATING}</li>
-         * </ul>
-         *
-         * @param key The key for referencing this value
-         * @param value The String value to store
-         * @return The Builder to allow chaining
-         */
-        public @NonNull Builder putRating(@NonNull @RatingKey String key, @Nullable Rating2 value) {
-            return mProvider.putRating_impl(key, value);
-        }
-
-        /**
-         * Put a {@link Bitmap} into the metadata. Custom keys may be used, but
-         * if the METADATA_KEYs defined in this class are used they may only be
-         * one of the following:
-         * <ul>
-         * <li>{@link #METADATA_KEY_ART}</li>
-         * <li>{@link #METADATA_KEY_ALBUM_ART}</li>
-         * <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
-         * </ul>
-         * Large bitmaps may be scaled down by the system when
-         * {@link android.media.session.MediaSession#setMetadata} is called.
-         * To pass full resolution images {@link Uri Uris} should be used with
-         * {@link #putString}.
-         *
-         * @param key The key for referencing this value
-         * @param value The Bitmap to store
-         * @return The Builder to allow chaining
-         */
-        public @NonNull Builder putBitmap(@NonNull @BitmapKey String key, @Nullable Bitmap value) {
-            return mProvider.putBitmap_impl(key, value);
-        }
-
-        /**
-         * Put a float value into the metadata. Custom keys may be used.
-         *
-         * @param key The key for referencing this value
-         * @param value The float value to store
-         * @return The Builder to allow chaining
-         */
-        public @NonNull Builder putFloat(@NonNull @LongKey String key, float value) {
-            return mProvider.putFloat_impl(key, value);
-        }
-
-        /**
-         * Set a bundle of extras.
-         *
-         * @param extras The extras to include with this description or null.
-         * @return The Builder to allow chaining
-         */
-        public Builder setExtras(@Nullable Bundle extras) {
-            return mProvider.setExtras_impl(extras);
-        }
-
-        /**
-         * Creates a {@link MediaMetadata2} instance with the specified fields.
-         *
-         * @return The new MediaMetadata2 instance
-         */
-        public @NonNull MediaMetadata2 build() {
-            return mProvider.build_impl();
-        }
-    }
-}
-
diff --git a/media/java/android/media/MediaPlaylistAgent.java b/media/java/android/media/MediaPlaylistAgent.java
deleted file mode 100644
index 88f37e7..0000000
--- a/media/java/android/media/MediaPlaylistAgent.java
+++ /dev/null
@@ -1,357 +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.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.media.update.ApiLoader;
-import android.media.update.MediaPlaylistAgentProvider;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- * MediaPlaylistAgent is the abstract class an application needs to derive from to pass an object
- * to a MediaSession2 that will override default playlist handling behaviors. It contains a set of
- * notify methods to signal MediaSession2 that playlist-related state has changed.
- * <p>
- * Playlists are composed of one or multiple {@link MediaItem2} instances, which combine metadata
- * and data sources (as {@link DataSourceDesc})
- * Used by {@link MediaSession2} and {@link MediaController2}.
- */
-// This class only includes methods that contain {@link MediaItem2}.
-public abstract class MediaPlaylistAgent {
-    /**
-     * @hide
-     */
-    @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL,
-            REPEAT_MODE_GROUP})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RepeatMode {}
-
-    /**
-     * Playback will be stopped at the end of the playing media list.
-     */
-    public static final int REPEAT_MODE_NONE = 0;
-
-    /**
-     * Playback of the current playing media item will be repeated.
-     */
-    public static final int REPEAT_MODE_ONE = 1;
-
-    /**
-     * Playing media list will be repeated.
-     */
-    public static final int REPEAT_MODE_ALL = 2;
-
-    /**
-     * Playback of the playing media group will be repeated.
-     * A group is a logical block of media items which is specified in the section 5.7 of the
-     * Bluetooth AVRCP 1.6. An example of a group is the playlist.
-     */
-    public static final int REPEAT_MODE_GROUP = 3;
-
-    /**
-     * @hide
-     */
-    @IntDef({SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ShuffleMode {}
-
-    /**
-     * Media list will be played in order.
-     */
-    public static final int SHUFFLE_MODE_NONE = 0;
-
-    /**
-     * Media list will be played in shuffled order.
-     */
-    public static final int SHUFFLE_MODE_ALL = 1;
-
-    /**
-     * Media group will be played in shuffled order.
-     * A group is a logical block of media items which is specified in the section 5.7 of the
-     * Bluetooth AVRCP 1.6. An example of a group is the playlist.
-     */
-    public static final int SHUFFLE_MODE_GROUP = 2;
-
-    private final MediaPlaylistAgentProvider mProvider;
-
-    /**
-     * A callback class to receive notifications for events on the media player. See
-     * {@link MediaPlaylistAgent#registerPlaylistEventCallback(Executor, PlaylistEventCallback)}
-     * to register this callback.
-     */
-    public static abstract class PlaylistEventCallback {
-        /**
-         * Called when a playlist is changed.
-         *
-         * @param playlistAgent playlist agent for this event
-         * @param list new playlist
-         * @param metadata new metadata
-         */
-        public void onPlaylistChanged(@NonNull MediaPlaylistAgent playlistAgent,
-                @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) { }
-
-        /**
-         * Called when a playlist metadata is changed.
-         *
-         * @param playlistAgent playlist agent for this event
-         * @param metadata new metadata
-         */
-        public void onPlaylistMetadataChanged(@NonNull MediaPlaylistAgent playlistAgent,
-                @Nullable MediaMetadata2 metadata) { }
-
-        /**
-         * Called when the shuffle mode is changed.
-         *
-         * @param playlistAgent playlist agent for this event
-         * @param shuffleMode repeat mode
-         * @see #SHUFFLE_MODE_NONE
-         * @see #SHUFFLE_MODE_ALL
-         * @see #SHUFFLE_MODE_GROUP
-         */
-        public void onShuffleModeChanged(@NonNull MediaPlaylistAgent playlistAgent,
-                @ShuffleMode int shuffleMode) { }
-
-        /**
-         * Called when the repeat mode is changed.
-         *
-         * @param playlistAgent playlist agent for this event
-         * @param repeatMode repeat mode
-         * @see #REPEAT_MODE_NONE
-         * @see #REPEAT_MODE_ONE
-         * @see #REPEAT_MODE_ALL
-         * @see #REPEAT_MODE_GROUP
-         */
-        public void onRepeatModeChanged(@NonNull MediaPlaylistAgent playlistAgent,
-                @RepeatMode int repeatMode) { }
-    }
-
-    public MediaPlaylistAgent() {
-        mProvider = ApiLoader.getProvider().createMediaPlaylistAgent(this);
-    }
-
-    /**
-     * Register {@link PlaylistEventCallback} to listen changes in the underlying
-     * {@link MediaPlaylistAgent}.
-     *
-     * @param executor a callback Executor
-     * @param callback a PlaylistEventCallback
-     * @throws IllegalArgumentException if executor or callback is {@code null}.
-     */
-    public final void registerPlaylistEventCallback(
-            @NonNull @CallbackExecutor Executor executor, @NonNull PlaylistEventCallback callback) {
-        mProvider.registerPlaylistEventCallback_impl(executor, callback);
-    }
-
-    /**
-     * Unregister the previously registered {@link PlaylistEventCallback}.
-     *
-     * @param callback the callback to be removed
-     * @throws IllegalArgumentException if the callback is {@code null}.
-     */
-    public final void unregisterPlaylistEventCallback(@NonNull PlaylistEventCallback callback) {
-        mProvider.unregisterPlaylistEventCallback_impl(callback);
-    }
-
-    public final void notifyPlaylistChanged() {
-        mProvider.notifyPlaylistChanged_impl();
-    }
-
-    public final void notifyPlaylistMetadataChanged() {
-        mProvider.notifyPlaylistMetadataChanged_impl();
-    }
-
-    public final void notifyShuffleModeChanged() {
-        mProvider.notifyShuffleModeChanged_impl();
-    }
-
-    public final void notifyRepeatModeChanged() {
-        mProvider.notifyRepeatModeChanged_impl();
-    }
-
-    /**
-     * Returns the playlist
-     *
-     * @return playlist, or null if none is set.
-     */
-    public @Nullable List<MediaItem2> getPlaylist() {
-        return mProvider.getPlaylist_impl();
-    }
-
-    /**
-     * Sets the playlist.
-     *
-     * @param list playlist
-     * @param metadata metadata of the playlist
-     */
-    public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
-        mProvider.setPlaylist_impl(list, metadata);
-    }
-
-    /**
-     * Returns the playlist metadata
-     *
-     * @return metadata metadata of the playlist, or null if none is set
-     */
-    public @Nullable MediaMetadata2 getPlaylistMetadata() {
-        return mProvider.getPlaylistMetadata_impl();
-    }
-
-    /**
-     * Updates the playlist metadata
-     *
-     * @param metadata metadata of the playlist
-     */
-    public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
-        mProvider.updatePlaylistMetadata_impl(metadata);
-    }
-
-    /**
-     * Adds the media item to the playlist at position index. Index equals or greater than
-     * the current playlist size will add the item at the end of the playlist.
-     * <p>
-     * This will not change the currently playing media item.
-     * If index is less than or equal to the current index of the playlist,
-     * the current index of the playlist will be incremented correspondingly.
-     *
-     * @param index the index you want to add
-     * @param item the media item you want to add
-     */
-    public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
-        mProvider.addPlaylistItem_impl(index, item);
-    }
-
-    /**
-     * Removes the media item from the playlist
-     *
-     * @param item media item to remove
-     */
-    public void removePlaylistItem(@NonNull MediaItem2 item) {
-        mProvider.removePlaylistItem_impl(item);
-    }
-
-    /**
-     * Replace the media item at index in the playlist. This can be also used to update metadata of
-     * an item.
-     *
-     * @param index the index of the item to replace
-     * @param item the new item
-     */
-    public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
-        mProvider.replacePlaylistItem_impl(index, item);
-    }
-
-    /**
-     * Skips to the the media item, and plays from it.
-     *
-     * @param item media item to start playing from
-     */
-    public void skipToPlaylistItem(@NonNull MediaItem2 item) {
-        mProvider.skipToPlaylistItem_impl(item);
-    }
-
-    /**
-     * Skips to the previous item in the playlist.
-     */
-    public void skipToPreviousItem() {
-        mProvider.skipToPreviousItem_impl();
-    }
-
-    /**
-     * Skips to the next item in the playlist.
-     */
-    public void skipToNextItem() {
-        mProvider.skipToNextItem_impl();
-    }
-
-    /**
-     * Gets the repeat mode
-     *
-     * @return repeat mode
-     * @see #REPEAT_MODE_NONE
-     * @see #REPEAT_MODE_ONE
-     * @see #REPEAT_MODE_ALL
-     * @see #REPEAT_MODE_GROUP
-     */
-    public @RepeatMode int getRepeatMode() {
-        return mProvider.getRepeatMode_impl();
-    }
-
-    /**
-     * Sets the repeat mode
-     *
-     * @param repeatMode repeat mode
-     * @see #REPEAT_MODE_NONE
-     * @see #REPEAT_MODE_ONE
-     * @see #REPEAT_MODE_ALL
-     * @see #REPEAT_MODE_GROUP
-     */
-    public void setRepeatMode(@RepeatMode int repeatMode) {
-        mProvider.setRepeatMode_impl(repeatMode);
-    }
-
-    /**
-     * Gets the shuffle mode
-     *
-     * @return The shuffle mode
-     * @see #SHUFFLE_MODE_NONE
-     * @see #SHUFFLE_MODE_ALL
-     * @see #SHUFFLE_MODE_GROUP
-     */
-    public @ShuffleMode int getShuffleMode() {
-        return mProvider.getShuffleMode_impl();
-    }
-
-    /**
-     * Sets the shuffle mode
-     *
-     * @param shuffleMode The shuffle mode
-     * @see #SHUFFLE_MODE_NONE
-     * @see #SHUFFLE_MODE_ALL
-     * @see #SHUFFLE_MODE_GROUP
-     */
-    public void setShuffleMode(@ShuffleMode int shuffleMode) {
-        mProvider.setShuffleMode_impl(shuffleMode);
-    }
-
-    /**
-     * Called by {@link MediaSession2} when it wants to translate {@link DataSourceDesc} from the
-     * {@link MediaPlayerBase.PlayerEventCallback} to the {@link MediaItem2}. Override this method
-     * if you want to create {@link DataSourceDesc}s dynamically, instead of specifying them with
-     * {@link #setPlaylist(List, MediaMetadata2)}.
-     * <p>
-     * Session would throw an exception if this returns {@code null} for {@param dsd} from the
-     * {@link MediaPlayerBase.PlayerEventCallback}.
-     * <p>
-     * Default implementation calls the {@link #getPlaylist()} and searches the {@link MediaItem2}
-     * with the {@param dsd}.
-     *
-     * @param dsd The dsd to query.
-     * @return A {@link MediaItem2} object in the playlist that matches given {@code dsd}.
-     * @throws IllegalArgumentException if {@code dsd} is null
-     */
-    public @Nullable MediaItem2 getMediaItem(@NonNull DataSourceDesc dsd) {
-        return mProvider.getMediaItem_impl(dsd);
-    }
-}
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
deleted file mode 100644
index 9e97125..0000000
--- a/media/java/android/media/MediaSession2.java
+++ /dev/null
@@ -1,1388 +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.MediaPlayerBase.BUFFERING_STATE_UNKNOWN;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.media.MediaPlayerBase.BuffState;
-import android.media.MediaPlayerBase.PlayerState;
-import android.media.MediaPlaylistAgent.RepeatMode;
-import android.media.MediaPlaylistAgent.ShuffleMode;
-import android.media.update.ApiLoader;
-import android.media.update.MediaSession2Provider;
-import android.media.update.MediaSession2Provider.BuilderBaseProvider;
-import android.media.update.MediaSession2Provider.CommandButtonProvider;
-import android.media.update.MediaSession2Provider.ControllerInfoProvider;
-import android.media.update.ProviderCreator;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IInterface;
-import android.os.ResultReceiver;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- * Allows a media app to expose its transport controls and playback information in a process to
- * other processes including the Android framework and other apps. Common use cases are as follows.
- * <ul>
- *     <li>Bluetooth/wired headset key events support</li>
- *     <li>Android Auto/Wearable support</li>
- *     <li>Separating UI process and playback process</li>
- * </ul>
- * <p>
- * A MediaSession2 should be created when an app wants to publish media playback information or
- * handle media keys. In general an app only needs one session for all playback, though multiple
- * sessions can be created to provide finer grain controls of media.
- * <p>
- * A session can be obtained by {@link Builder}. The owner of the session may pass its session token
- * to other processes to allow them to create a {@link MediaController2} to interact with the
- * session.
- * <p>
- * When a session receive transport control commands, the session sends the commands directly to
- * the the underlying media player set by {@link Builder} or
- * {@link #updatePlayer}.
- * <p>
- * When an app is finished performing playback it must call {@link #close()} to clean up the session
- * and notify any controllers.
- * <p>
- * {@link MediaSession2} objects should be used on the thread on the looper.
- */
-public class MediaSession2 implements AutoCloseable {
-    private final MediaSession2Provider mProvider;
-
-    /**
-     * @hide
-     */
-    @IntDef({ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_APP_ERROR, ERROR_CODE_NOT_SUPPORTED,
-            ERROR_CODE_AUTHENTICATION_EXPIRED, ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED,
-            ERROR_CODE_CONCURRENT_STREAM_LIMIT, ERROR_CODE_PARENTAL_CONTROL_RESTRICTED,
-            ERROR_CODE_NOT_AVAILABLE_IN_REGION, ERROR_CODE_CONTENT_ALREADY_PLAYING,
-            ERROR_CODE_SKIP_LIMIT_REACHED, ERROR_CODE_ACTION_ABORTED, ERROR_CODE_END_OF_QUEUE,
-            ERROR_CODE_SETUP_REQUIRED})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ErrorCode {}
-
-    /**
-     * This is the default error code and indicates that none of the other error codes applies.
-     */
-    public static final int ERROR_CODE_UNKNOWN_ERROR = 0;
-
-    /**
-     * Error code when the application state is invalid to fulfill the request.
-     */
-    public static final int ERROR_CODE_APP_ERROR = 1;
-
-    /**
-     * Error code when the request is not supported by the application.
-     */
-    public static final int ERROR_CODE_NOT_SUPPORTED = 2;
-
-    /**
-     * Error code when the request cannot be performed because authentication has expired.
-     */
-    public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3;
-
-    /**
-     * Error code when a premium account is required for the request to succeed.
-     */
-    public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4;
-
-    /**
-     * Error code when too many concurrent streams are detected.
-     */
-    public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5;
-
-    /**
-     * Error code when the content is blocked due to parental controls.
-     */
-    public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6;
-
-    /**
-     * Error code when the content is blocked due to being regionally unavailable.
-     */
-    public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7;
-
-    /**
-     * Error code when the requested content is already playing.
-     */
-    public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8;
-
-    /**
-     * Error code when the application cannot skip any more songs because skip limit is reached.
-     */
-    public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9;
-
-    /**
-     * Error code when the action is interrupted due to some external event.
-     */
-    public static final int ERROR_CODE_ACTION_ABORTED = 10;
-
-    /**
-     * Error code when the playback navigation (previous, next) is not possible because the queue
-     * was exhausted.
-     */
-    public static final int ERROR_CODE_END_OF_QUEUE = 11;
-
-    /**
-     * Error code when the session needs user's manual intervention.
-     */
-    public static final int ERROR_CODE_SETUP_REQUIRED = 12;
-
-    /**
-     * Interface definition of a callback to be invoked when a {@link MediaItem2} in the playlist
-     * didn't have a {@link DataSourceDesc} but it's needed now for preparing or playing it.
-     *
-     * #see #setOnDataSourceMissingHelper
-     */
-    public interface OnDataSourceMissingHelper {
-        /**
-         * Called when a {@link MediaItem2} in the playlist didn't have a {@link DataSourceDesc}
-         * but it's needed now for preparing or playing it. Returned data source descriptor will be
-         * sent to the player directly to prepare or play the contents.
-         * <p>
-         * An exception may be thrown if the returned {@link DataSourceDesc} is duplicated in the
-         * playlist, so items cannot be differentiated.
-         *
-         * @param session the session for this event
-         * @param item media item from the controller
-         * @return a data source descriptor if the media item. Can be {@code null} if the content
-         *        isn't available.
-         */
-        @Nullable DataSourceDesc onDataSourceMissing(@NonNull MediaSession2 session,
-                @NonNull MediaItem2 item);
-    }
-
-    /**
-     * Callback to be called for all incoming commands from {@link MediaController2}s.
-     * <p>
-     * If it's not set, the session will accept all controllers and all incoming commands by
-     * default.
-     */
-    // TODO(jaewan): Move this to updatable for default implementation (b/74091963)
-    public static abstract class SessionCallback {
-        /**
-         * Called when a controller is created for this session. Return allowed commands for
-         * controller. By default it allows all connection requests and commands.
-         * <p>
-         * You can reject the connection by return {@code null}. In that case, controller receives
-         * {@link MediaController2.ControllerCallback#onDisconnected(MediaController2)} and cannot
-         * be usable.
-         *
-         * @param session the session for this event
-         * @param controller controller information.
-         * @return allowed commands. Can be {@code null} to reject connection.
-         */
-        public @Nullable SessionCommandGroup2 onConnect(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller) {
-            SessionCommandGroup2 commands = new SessionCommandGroup2();
-            commands.addAllPredefinedCommands();
-            return commands;
-        }
-
-        /**
-         * 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 command that will be sent directly to the player. Return
-         * {@code false} here to reject the request and stop sending command to the player.
-         *
-         * @param session the session for this event
-         * @param controller controller information.
-         * @param command a command. This method will be called for every single command.
-         * @return {@code true} if you want to accept incoming command. {@code false} otherwise.
-         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PLAY
-         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PAUSE
-         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_STOP
-         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM
-         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM
-         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PREPARE
-         * @see SessionCommand2#COMMAND_CODE_SESSION_FAST_FORWARD
-         * @see SessionCommand2#COMMAND_CODE_SESSION_REWIND
-         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_SEEK_TO
-         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM
-         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
-         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REMOVE_ITEM
-         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_GET_LIST
-         * @see SessionCommand2#COMMAND_CODE_SET_VOLUME
-         */
-        public boolean onCommandRequest(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull SessionCommand2 command) {
-            return true;
-        }
-
-        /**
-         * Called when a controller set rating of a media item through
-         * {@link MediaController2#setRating(String, Rating2)}.
-         * <p>
-         * To allow setting user rating for a {@link MediaItem2}, the media item's metadata
-         * should have {@link Rating2} with the key {@link MediaMetadata#METADATA_KEY_USER_RATING},
-         * in order to provide possible rating style for controller. Controller will follow the
-         * rating style.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param mediaId media id from the controller
-         * @param rating new rating from the controller
-         */
-        public void onSetRating(@NonNull MediaSession2 session, @NonNull ControllerInfo controller,
-                @NonNull String mediaId, @NonNull Rating2 rating) { }
-
-        /**
-         * Called when a controller sent a custom command through
-         * {@link MediaController2#sendCustomCommand(SessionCommand2, Bundle, ResultReceiver)}.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param customCommand custom command.
-         * @param args optional arguments
-         * @param cb optional result receiver
-         */
-        public void onCustomCommand(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull SessionCommand2 customCommand,
-                @Nullable Bundle args, @Nullable ResultReceiver cb) { }
-
-        /**
-         * Called when a controller requested to play a specific mediaId through
-         * {@link MediaController2#playFromMediaId(String, Bundle)}.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param mediaId media id
-         * @param extras optional extra bundle
-         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID
-         */
-        public void onPlayFromMediaId(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull String mediaId,
-                @Nullable Bundle extras) { }
-
-        /**
-         * Called when a controller requested to begin playback from a search query through
-         * {@link MediaController2#playFromSearch(String, Bundle)}
-         * <p>
-         * An empty query indicates that the app may play any music. The implementation should
-         * attempt to make a smart choice about what to play.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param query query string. Can be empty to indicate any suggested media
-         * @param extras optional extra bundle
-         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_SEARCH
-         */
-        public void onPlayFromSearch(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull String query,
-                @Nullable Bundle extras) { }
-
-        /**
-         * Called when a controller requested to play a specific media item represented by a URI
-         * through {@link MediaController2#playFromUri(Uri, Bundle)}
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param uri uri
-         * @param extras optional extra bundle
-         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_URI
-         */
-        public void onPlayFromUri(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull Uri uri,
-                @Nullable Bundle extras) { }
-
-        /**
-         * Called when a controller requested to prepare for playing a specific mediaId through
-         * {@link MediaController2#prepareFromMediaId(String, Bundle)}.
-         * <p>
-         * During the preparation, a session should not hold audio focus in order to allow other
-         * sessions play seamlessly. The state of playback should be updated to
-         * {@link MediaPlayerBase#PLAYER_STATE_PAUSED} after the preparation is done.
-         * <p>
-         * The playback of the prepared content should start in the later calls of
-         * {@link MediaSession2#play()}.
-         * <p>
-         * Override {@link #onPlayFromMediaId} to handle requests for starting
-         * playback without preparation.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param mediaId media id to prepare
-         * @param extras optional extra bundle
-         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID
-         */
-        public void onPrepareFromMediaId(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull String mediaId,
-                @Nullable Bundle extras) { }
-
-        /**
-         * Called when a controller requested to prepare playback from a search query through
-         * {@link MediaController2#prepareFromSearch(String, Bundle)}.
-         * <p>
-         * An empty query indicates that the app may prepare any music. The implementation should
-         * attempt to make a smart choice about what to play.
-         * <p>
-         * The state of playback should be updated to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}
-         * after the preparation is done. The playback of the prepared content should start in the
-         * later calls of {@link MediaSession2#play()}.
-         * <p>
-         * Override {@link #onPlayFromSearch} to handle requests for starting playback without
-         * preparation.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param query query string. Can be empty to indicate any suggested media
-         * @param extras optional extra bundle
-         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH
-         */
-        public void onPrepareFromSearch(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull String query,
-                @Nullable Bundle extras) { }
-
-        /**
-         * Called when a controller requested to prepare a specific media item represented by a URI
-         * through {@link MediaController2#prepareFromUri(Uri, Bundle)}.
-         * <p>
-         * During the preparation, a session should not hold audio focus in order to allow
-         * other sessions play seamlessly. The state of playback should be updated to
-         * {@link MediaPlayerBase#PLAYER_STATE_PAUSED} after the preparation is done.
-         * <p>
-         * The playback of the prepared content should start in the later calls of
-         * {@link MediaSession2#play()}.
-         * <p>
-         * Override {@link #onPlayFromUri} to handle requests for starting playback without
-         * preparation.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param uri uri
-         * @param extras optional extra bundle
-         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_URI
-         */
-        public void onPrepareFromUri(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull Uri uri, @Nullable Bundle extras) { }
-
-        /**
-         * Called when a controller called {@link MediaController2#fastForward()}
-         *
-         * @param session the session for this event
-         */
-        public void onFastForward(@NonNull MediaSession2 session) { }
-
-        /**
-         * Called when a controller called {@link MediaController2#rewind()}
-         *
-         * @param session the session for this event
-         */
-        public void onRewind(@NonNull MediaSession2 session) { }
-
-        /**
-         * Called when the player's current playing item is changed
-         * <p>
-         * When it's called, you should invalidate previous playback information and wait for later
-         * callbacks.
-         *
-         * @param session the controller for this event
-         * @param player the player for this event
-         * @param item new item
-         */
-        // TODO(jaewan): Use this (b/74316764)
-        public void onCurrentMediaItemChanged(@NonNull MediaSession2 session,
-                @NonNull MediaPlayerBase player, @NonNull MediaItem2 item) { }
-
-        /**
-         * Called when the player is <i>prepared</i>, i.e. it is ready to play the content
-         * referenced by the given data source.
-         * @param session the session for this event
-         * @param player the player for this event
-         * @param item the media item for which buffering is happening
-         */
-        public void onMediaPrepared(@NonNull MediaSession2 session, @NonNull MediaPlayerBase player,
-                @NonNull MediaItem2 item) { }
-
-        /**
-         * Called to indicate that the state of the player has changed.
-         * See {@link MediaPlayerBase#getPlayerState()} for polling the player state.
-         * @param session the session for this event
-         * @param player the player for this event
-         * @param state the new state of the player.
-         */
-        public void onPlayerStateChanged(@NonNull MediaSession2 session,
-                @NonNull MediaPlayerBase player, @PlayerState int state) { }
-
-        /**
-         * Called to report buffering events for a data source.
-         *
-         * @param session the session for this event
-         * @param player the player for this event
-         * @param item the media item for which buffering is happening.
-         * @param state the new buffering state.
-         */
-        public void onBufferingStateChanged(@NonNull MediaSession2 session,
-                @NonNull MediaPlayerBase player, @NonNull MediaItem2 item, @BuffState int state) { }
-
-        /**
-         * Called to indicate that the playback speed has changed.
-         * @param session the session for this event
-         * @param player the player for this event
-         * @param speed the new playback speed.
-         */
-        public void onPlaybackSpeedChanged(@NonNull MediaSession2 session,
-                @NonNull MediaPlayerBase player, float speed) { }
-
-        /**
-         * Called to indicate that {@link #seekTo(long)} is completed.
-         *
-         * @param session the session for this event.
-         * @param mpb the player that has completed seeking.
-         * @param position the previous seeking request.
-         * @see #seekTo(long)
-         */
-        public void onSeekCompleted(@NonNull MediaSession2 session, @NonNull MediaPlayerBase mpb,
-                long position) { }
-
-        /**
-         * Called when a playlist is changed from the {@link MediaPlaylistAgent}.
-         * <p>
-         * This is called when the underlying agent has called
-         * {@link MediaPlaylistAgent.PlaylistEventCallback#onPlaylistChanged(MediaPlaylistAgent,
-         * List, MediaMetadata2)}.
-         *
-         * @param session the session for this event
-         * @param playlistAgent playlist agent for this event
-         * @param list new playlist
-         * @param metadata new metadata
-         */
-        public void onPlaylistChanged(@NonNull MediaSession2 session,
-                @NonNull MediaPlaylistAgent playlistAgent, @NonNull List<MediaItem2> list,
-                @Nullable MediaMetadata2 metadata) { }
-
-        /**
-         * Called when a playlist metadata is changed.
-         *
-         * @param session the session for this event
-         * @param playlistAgent playlist agent for this event
-         * @param metadata new metadata
-         */
-        public void onPlaylistMetadataChanged(@NonNull MediaSession2 session,
-                @NonNull MediaPlaylistAgent playlistAgent, @Nullable MediaMetadata2 metadata) { }
-
-        /**
-         * Called when the shuffle mode is changed.
-         *
-         * @param session the session for this event
-         * @param playlistAgent playlist agent for this event
-         * @param shuffleMode repeat mode
-         * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
-         * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
-         * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
-         */
-        public void onShuffleModeChanged(@NonNull MediaSession2 session,
-                @NonNull MediaPlaylistAgent playlistAgent,
-                @MediaPlaylistAgent.ShuffleMode int shuffleMode) { }
-
-        /**
-         * Called when the repeat mode is changed.
-         *
-         * @param session the session for this event
-         * @param playlistAgent playlist agent for this event
-         * @param repeatMode repeat mode
-         * @see MediaPlaylistAgent#REPEAT_MODE_NONE
-         * @see MediaPlaylistAgent#REPEAT_MODE_ONE
-         * @see MediaPlaylistAgent#REPEAT_MODE_ALL
-         * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
-         */
-        public void onRepeatModeChanged(@NonNull MediaSession2 session,
-                @NonNull MediaPlaylistAgent playlistAgent,
-                @MediaPlaylistAgent.RepeatMode int repeatMode) { }
-    }
-
-    /**
-     * Base builder class for MediaSession2 and its subclass. Any change in this class should be
-     * also applied to the subclasses {@link MediaSession2.Builder} and
-     * {@link MediaLibraryService2.MediaLibrarySession.Builder}.
-     * <p>
-     * APIs here should be package private, but should have documentations for developers.
-     * Otherwise, javadoc will generate documentation with the generic types such as follows.
-     * <pre>U extends BuilderBase<T, U, C> setSessionCallback(Executor executor, C callback)</pre>
-     * <p>
-     * This class is hidden to prevent from generating test stub, which fails with
-     * 'unexpected bound' because it tries to auto generate stub class as follows.
-     * <pre>abstract static class BuilderBase<
-     *      T extends android.media.MediaSession2,
-     *      U extends android.media.MediaSession2.BuilderBase<
-     *              T, U, C extends android.media.MediaSession2.SessionCallback>, C></pre>
-     * @hide
-     */
-    static abstract class BuilderBase
-            <T extends MediaSession2, U extends BuilderBase<T, U, C>, C extends SessionCallback> {
-        private final BuilderBaseProvider<T, C> mProvider;
-
-        BuilderBase(ProviderCreator<BuilderBase<T, U, C>, BuilderBaseProvider<T, C>> creator) {
-            mProvider = creator.createProvider(this);
-        }
-
-        /**
-         * Sets the underlying {@link MediaPlayerBase} for this session to dispatch incoming event
-         * to.
-         *
-         * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
-         */
-        @NonNull U setPlayer(@NonNull MediaPlayerBase player) {
-            mProvider.setPlayer_impl(player);
-            return (U) this;
-        }
-
-        /**
-         * Sets the {@link MediaPlaylistAgent} for this session to manages playlist of the
-         * underlying {@link MediaPlayerBase}. The playlist agent should manage
-         * {@link MediaPlayerBase} for calling {@link MediaPlayerBase#setNextDataSources(List)}.
-         * <p>
-         * If the {@link MediaPlaylistAgent} isn't set, session will create the default playlist
-         * agent.
-         *
-         * @param playlistAgent a {@link MediaPlaylistAgent} that manages playlist of the
-         *                      {@code player}
-         */
-        U setPlaylistAgent(@NonNull MediaPlaylistAgent playlistAgent) {
-            mProvider.setPlaylistAgent_impl(playlistAgent);
-            return (U) this;
-        }
-
-        /**
-         * Sets the {@link VolumeProvider2} for this session to handle volume events. If not set,
-         * system will adjust the appropriate stream volume for this session's player.
-         *
-         * @param volumeProvider The provider that will receive volume button events.
-         */
-        @NonNull U setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
-            mProvider.setVolumeProvider_impl(volumeProvider);
-            return (U) this;
-        }
-
-        /**
-         * 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.
-         */
-        @NonNull U setSessionActivity(@Nullable PendingIntent pi) {
-            mProvider.setSessionActivity_impl(pi);
-            return (U) this;
-        }
-
-        /**
-         * Set ID of the session. If it's not set, an empty string with used to create a session.
-         * <p>
-         * Use this if and only if your app supports multiple playback at the same time and also
-         * wants to provide external apps to have finer controls of them.
-         *
-         * @param id id of the session. Must be unique per package.
-         * @throws IllegalArgumentException if id is {@code null}
-         * @return
-         */
-        @NonNull U setId(@NonNull String id) {
-            mProvider.setId_impl(id);
-            return (U) this;
-        }
-
-        /**
-         * Set callback for the session.
-         *
-         * @param executor callback executor
-         * @param callback session callback.
-         * @return
-         */
-        @NonNull U setSessionCallback(@NonNull @CallbackExecutor Executor executor,
-                @NonNull C callback) {
-            mProvider.setSessionCallback_impl(executor, callback);
-            return (U) this;
-        }
-
-        /**
-         * Build {@link MediaSession2}.
-         *
-         * @return a new session
-         * @throws IllegalStateException if the session with the same id is already exists for the
-         *      package.
-         */
-        @NonNull T build() {
-            return mProvider.build_impl();
-        }
-    }
-
-    /**
-     * Builder for {@link MediaSession2}.
-     * <p>
-     * Any incoming event from the {@link MediaController2} will be handled on the thread
-     * that created session with the {@link Builder#build()}.
-     */
-    // Override all methods just to show them with the type instead of generics in Javadoc.
-    // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
-    public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
-        public Builder(Context context) {
-            super((instance) -> ApiLoader.getProvider().createMediaSession2Builder(
-                    context, (Builder) instance));
-        }
-
-        @Override
-        public @NonNull Builder setPlayer(@NonNull MediaPlayerBase player) {
-            return super.setPlayer(player);
-        }
-
-        @Override
-        public Builder setPlaylistAgent(@NonNull MediaPlaylistAgent playlistAgent) {
-            return super.setPlaylistAgent(playlistAgent);
-        }
-
-        @Override
-        public @NonNull Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
-            return super.setVolumeProvider(volumeProvider);
-        }
-
-        @Override
-        public @NonNull Builder setSessionActivity(@Nullable PendingIntent pi) {
-            return super.setSessionActivity(pi);
-        }
-
-        @Override
-        public @NonNull Builder setId(@NonNull String id) {
-            return super.setId(id);
-        }
-
-        @Override
-        public @NonNull Builder setSessionCallback(@NonNull Executor executor,
-                @Nullable SessionCallback callback) {
-            return super.setSessionCallback(executor, callback);
-        }
-
-        @Override
-        public @NonNull MediaSession2 build() {
-            return super.build();
-        }
-    }
-
-    /**
-     * Information of a controller.
-     */
-    public static final class ControllerInfo {
-        private final ControllerInfoProvider mProvider;
-
-        /**
-         * @hide
-         */
-        public ControllerInfo(@NonNull Context context, int uid, int pid,
-                @NonNull String packageName, @NonNull IInterface callback) {
-            mProvider = ApiLoader.getProvider().createMediaSession2ControllerInfo(
-                    context, this, uid, pid, packageName, callback);
-        }
-
-        /**
-         * @return package name of the controller
-         */
-        public @NonNull String getPackageName() {
-            return mProvider.getPackageName_impl();
-        }
-
-        /**
-         * @return uid of the controller
-         */
-        public int getUid() {
-            return mProvider.getUid_impl();
-        }
-
-        /**
-         * Return if the controller has granted {@code android.permission.MEDIA_CONTENT_CONTROL} or
-         * has a enabled notification listener so can be trusted to accept connection and incoming
-         * command request.
-         *
-         * @return {@code true} if the controller is trusted.
-         */
-        public boolean isTrusted() {
-            return mProvider.isTrusted_impl();
-        }
-
-        /**
-         * @hide
-         */
-        public @NonNull ControllerInfoProvider getProvider() {
-            return mProvider;
-        }
-
-        @Override
-        public int hashCode() {
-            return mProvider.hashCode_impl();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            return mProvider.equals_impl(obj);
-        }
-
-        @Override
-        public String toString() {
-            return mProvider.toString_impl();
-        }
-    }
-
-    /**
-     * Button for a {@link SessionCommand2} that will be shown by the controller.
-     * <p>
-     * It's up to the controller's decision to respect or ignore this customization request.
-     */
-    public static final class CommandButton {
-        private final CommandButtonProvider mProvider;
-
-        /**
-         * @hide
-         */
-        public CommandButton(CommandButtonProvider provider) {
-            mProvider = provider;
-        }
-
-        /**
-         * Get command associated with this button. Can be {@code null} if the button isn't enabled
-         * and only providing placeholder.
-         *
-         * @return command or {@code null}
-         */
-        public @Nullable
-        SessionCommand2 getCommand() {
-            return mProvider.getCommand_impl();
-        }
-
-        /**
-         * Resource id of the button in this package. Can be {@code 0} if the command is predefined
-         * and custom icon isn't needed.
-         *
-         * @return resource id of the icon. Can be {@code 0}.
-         */
-        public int getIconResId() {
-            return mProvider.getIconResId_impl();
-        }
-
-        /**
-         * Display name of the button. Can be {@code null} or empty if the command is predefined
-         * and custom name isn't needed.
-         *
-         * @return custom display name. Can be {@code null} or empty.
-         */
-        public @Nullable String getDisplayName() {
-            return mProvider.getDisplayName_impl();
-        }
-
-        /**
-         * Extra information of the button. It's private information between session and controller.
-         *
-         * @return
-         */
-        public @Nullable Bundle getExtras() {
-            return mProvider.getExtras_impl();
-        }
-
-        /**
-         * Return whether it's enabled
-         *
-         * @return {@code true} if enabled. {@code false} otherwise.
-         */
-        public boolean isEnabled() {
-            return mProvider.isEnabled_impl();
-        }
-
-        /**
-         * @hide
-         */
-        public @NonNull CommandButtonProvider getProvider() {
-            return mProvider;
-        }
-
-        /**
-         * Builder for {@link CommandButton}.
-         */
-        public static final class Builder {
-            private final CommandButtonProvider.BuilderProvider mProvider;
-
-            public Builder() {
-                mProvider = ApiLoader.getProvider().createMediaSession2CommandButtonBuilder(this);
-            }
-
-            public @NonNull Builder setCommand(@Nullable SessionCommand2 command) {
-                return mProvider.setCommand_impl(command);
-            }
-
-            public @NonNull Builder setIconResId(int resId) {
-                return mProvider.setIconResId_impl(resId);
-            }
-
-            public @NonNull Builder setDisplayName(@Nullable String displayName) {
-                return mProvider.setDisplayName_impl(displayName);
-            }
-
-            public @NonNull Builder setEnabled(boolean enabled) {
-                return mProvider.setEnabled_impl(enabled);
-            }
-
-            public @NonNull Builder setExtras(@Nullable Bundle extras) {
-                return mProvider.setExtras_impl(extras);
-            }
-
-            public @NonNull CommandButton build() {
-                return mProvider.build_impl();
-            }
-        }
-    }
-
-    /**
-     * Constructor is hidden and apps can only instantiate indirectly through {@link Builder}.
-     * <p>
-     * This intended behavior and here's the reasons.
-     *    1. Prevent multiple sessions with the same tag in a media app.
-     *       Whenever it happens only one session was properly setup and others were all dummies.
-     *       Android framework couldn't find the right session to dispatch media key event.
-     *    2. Simplify session's lifecycle.
-     *       {@link android.media.session.MediaSession} is available after all of
-     *       {@link android.media.session.MediaSession#setFlags(int)},
-     *       {@link android.media.session.MediaSession#setCallback(
-     *              android.media.session.MediaSession.Callback)},
-     *       and {@link android.media.session.MediaSession#setActive(boolean)}.
-     *       It was common for an app to omit one, so framework had to add heuristics to figure out
-     *       which should be the highest priority for handling media key event.
-     * @hide
-     */
-    public MediaSession2(MediaSession2Provider provider) {
-        super();
-        mProvider = provider;
-    }
-
-    /**
-     * @hide
-     */
-    public @NonNull MediaSession2Provider getProvider() {
-        return mProvider;
-    }
-
-    /**
-     * Sets the underlying {@link MediaPlayerBase} and {@link MediaPlaylistAgent} for this session
-     * to dispatch incoming event to.
-     * <p>
-     * When a {@link MediaPlaylistAgent} is specified here, the playlist agent should manage
-     * {@link MediaPlayerBase} for calling {@link MediaPlayerBase#setNextDataSources(List)}.
-     * <p>
-     * If the {@link MediaPlaylistAgent} isn't set, session will recreate the default playlist
-     * agent.
-     *
-     * @param player a {@link MediaPlayerBase} that handles actual media playback in your app
-     * @param playlistAgent a {@link MediaPlaylistAgent} that manages playlist of the {@code player}
-     * @param volumeProvider a {@link VolumeProvider2}. If {@code null}, system will adjust the
-     *                       appropriate stream volume for this session's player.
-     */
-    public void updatePlayer(@NonNull MediaPlayerBase player,
-            @Nullable MediaPlaylistAgent playlistAgent, @Nullable VolumeProvider2 volumeProvider) {
-        mProvider.updatePlayer_impl(player, playlistAgent, volumeProvider);
-    }
-
-    @Override
-    public void close() {
-        mProvider.close_impl();
-    }
-
-    /**
-     * @return player
-     */
-    public @NonNull MediaPlayerBase getPlayer() {
-        return mProvider.getPlayer_impl();
-    }
-
-    /**
-     * @return playlist agent
-     */
-    public @NonNull MediaPlaylistAgent getPlaylistAgent() {
-        return mProvider.getPlaylistAgent_impl();
-    }
-
-    /**
-     * @return volume provider
-     */
-    public @Nullable VolumeProvider2 getVolumeProvider() {
-        return mProvider.getVolumeProvider_impl();
-    }
-
-    /**
-     * Returns the {@link SessionToken2} for creating {@link MediaController2}.
-     */
-    public @NonNull
-    SessionToken2 getToken() {
-        return mProvider.getToken_impl();
-    }
-
-    public @NonNull List<ControllerInfo> getConnectedControllers() {
-        return mProvider.getConnectedControllers_impl();
-    }
-
-    /**
-     * Set the {@link AudioFocusRequest} to obtain the audio focus
-     *
-     * @param afr the full request parameters
-     */
-    public void setAudioFocusRequest(@Nullable AudioFocusRequest afr) {
-        // TODO(jaewan): implement this (b/72529899)
-        // mProvider.setAudioFocusRequest_impl(focusGain);
-    }
-
-    /**
-     * Sets ordered list of {@link CommandButton} for controllers to build UI with it.
-     * <p>
-     * It's up to controller's decision how to represent the layout in its own UI.
-     * Here's the same way
-     * (layout[i] means a CommandButton at index i in the given list)
-     * For 5 icons row
-     *      layout[3] layout[1] layout[0] layout[2] layout[4]
-     * For 3 icons row
-     *      layout[1] layout[0] layout[2]
-     * For 5 icons row with overflow icon (can show +5 extra buttons with overflow button)
-     *      expanded row:   layout[5] layout[6] layout[7] layout[8] layout[9]
-     *      main row:       layout[3] layout[1] layout[0] layout[2] layout[4]
-     * <p>
-     * This API can be called in the {@link SessionCallback#onConnect(
-     * MediaSession2, ControllerInfo)}.
-     *
-     * @param controller controller to specify layout.
-     * @param layout ordered list of layout.
-     */
-    public void setCustomLayout(@NonNull ControllerInfo controller,
-            @NonNull List<CommandButton> layout) {
-        mProvider.setCustomLayout_impl(controller, layout);
-    }
-
-    /**
-     * Set the new allowed command group for the controller
-     *
-     * @param controller controller to change allowed commands
-     * @param commands new allowed commands
-     */
-    public void setAllowedCommands(@NonNull ControllerInfo controller,
-            @NonNull SessionCommandGroup2 commands) {
-        mProvider.setAllowedCommands_impl(controller, commands);
-    }
-
-    /**
-     * Send custom command to all connected controllers.
-     *
-     * @param command a command
-     * @param args optional argument
-     */
-    public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args) {
-        mProvider.sendCustomCommand_impl(command, args);
-    }
-
-    /**
-     * Send custom command to a specific controller.
-     *
-     * @param command a command
-     * @param args optional argument
-     * @param receiver result receiver for the session
-     */
-    public void sendCustomCommand(@NonNull ControllerInfo controller,
-            @NonNull SessionCommand2 command, @Nullable Bundle args,
-            @Nullable ResultReceiver receiver) {
-        // Equivalent to the MediaController.sendCustomCommand(Action action, ResultReceiver r);
-        mProvider.sendCustomCommand_impl(controller, command, args, receiver);
-    }
-
-    /**
-     * Play playback
-     * <p>
-     * This calls {@link MediaPlayerBase#play()}.
-     */
-    public void play() {
-        mProvider.play_impl();
-    }
-
-    /**
-     * Pause playback.
-     * <p>
-     * This calls {@link MediaPlayerBase#pause()}.
-     */
-    public void pause() {
-        mProvider.pause_impl();
-    }
-
-    /**
-     * Stop playback, and reset the player to the initial state.
-     * <p>
-     * This calls {@link MediaPlayerBase#reset()}.
-     */
-    public void stop() {
-        mProvider.stop_impl();
-    }
-
-    /**
-     * Request that the player prepare its playback. In other words, other sessions can continue
-     * to play during the preparation of this session. This method can be used to speed up the
-     * start of the playback. Once the preparation is done, the session will change its playback
-     * state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards, {@link #play} can be called
-     * to start playback.
-     * <p>
-     * This calls {@link MediaPlayerBase#reset()}.
-     */
-    public void prepare() {
-        mProvider.prepare_impl();
-    }
-
-    /**
-     * Move to a new location in the media stream.
-     *
-     * @param pos Position to move to, in milliseconds.
-     */
-    public void seekTo(long pos) {
-        mProvider.seekTo_impl(pos);
-    }
-
-    /**
-     * @hide
-     */
-    public void skipForward() {
-        // To match with KEYCODE_MEDIA_SKIP_FORWARD
-    }
-
-    /**
-     * @hide
-     */
-    public void skipBackward() {
-        // To match with KEYCODE_MEDIA_SKIP_BACKWARD
-    }
-
-    /**
-     * Notify errors to the connected controllers
-     *
-     * @param errorCode error code
-     * @param extras extras
-     */
-    public void notifyError(@ErrorCode int errorCode, @Nullable Bundle extras) {
-        mProvider.notifyError_impl(errorCode, extras);
-    }
-
-    /**
-     * Gets the current player state.
-     *
-     * @return the current player state
-     */
-    public @PlayerState int getPlayerState() {
-        return mProvider.getPlayerState_impl();
-    }
-
-    /**
-     * Gets the current position.
-     *
-     * @return the current playback position in ms, or {@link MediaPlayerBase#UNKNOWN_TIME} if
-     *         unknown.
-     */
-    public long getCurrentPosition() {
-        return mProvider.getCurrentPosition_impl();
-    }
-
-    /**
-     * Gets the buffered position, or {@link MediaPlayerBase#UNKNOWN_TIME} if unknown.
-     *
-     * @return the buffered position in ms, or {@link MediaPlayerBase#UNKNOWN_TIME}.
-     */
-    public long getBufferedPosition() {
-        return mProvider.getBufferedPosition_impl();
-    }
-
-    /**
-     * Gets the current buffering state of the player.
-     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
-     * buffered.
-     *
-     * @return the buffering state.
-     */
-    public @BuffState int getBufferingState() {
-        // TODO(jaewan): Implement this
-        return BUFFERING_STATE_UNKNOWN;
-    }
-
-    /**
-     * Get the playback speed.
-     *
-     * @return speed
-     */
-    public float getPlaybackSpeed() {
-        // TODO(jaewan): implement this (b/74093080)
-        return -1;
-    }
-
-    /**
-     * Set the playback speed.
-     */
-    public void setPlaybackSpeed(float speed) {
-        // TODO(jaewan): implement this (b/74093080)
-    }
-
-    /**
-     * Sets the data source missing helper. Helper will be used to provide default implementation of
-     * {@link MediaPlaylistAgent} when it isn't set by developer.
-     * <p>
-     * Default implementation of the {@link MediaPlaylistAgent} will call helper when a
-     * {@link MediaItem2} in the playlist doesn't have a {@link DataSourceDesc}. This may happen
-     * when
-     * <ul>
-     *      <li>{@link MediaItem2} specified by {@link #setPlaylist(List, MediaMetadata2)} doesn't
-     *          have {@link DataSourceDesc}</li>
-     *      <li>{@link MediaController2#addPlaylistItem(int, MediaItem2)} is called and accepted
-     *          by {@link SessionCallback#onCommandRequest(
-     *          MediaSession2, ControllerInfo, SessionCommand2)}.
-     *          In that case, an item would be added automatically without the data source.</li>
-     * </ul>
-     * <p>
-     * If it's not set, playback wouldn't happen for the item without data source descriptor.
-     * <p>
-     * The helper will be run on the executor that was specified by
-     * {@link Builder#setSessionCallback(Executor, SessionCallback)}.
-     *
-     * @param helper a data source missing helper.
-     * @throws IllegalStateException when the helper is set when the playlist agent is set
-     * @see #setPlaylist(List, MediaMetadata2)
-     * @see SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)
-     * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
-     * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REPLACE_ITEM
-     */
-    public void setOnDataSourceMissingHelper(@NonNull OnDataSourceMissingHelper helper) {
-        mProvider.setOnDataSourceMissingHelper_impl(helper);
-    }
-
-    /**
-     * Clears the data source missing helper.
-     *
-     * @see #setOnDataSourceMissingHelper(OnDataSourceMissingHelper)
-     */
-    public void clearOnDataSourceMissingHelper() {
-        mProvider.clearOnDataSourceMissingHelper_impl();
-    }
-
-    /**
-     * Returns the playlist from the {@link MediaPlaylistAgent}.
-     * <p>
-     * This list may differ with the list that was specified with
-     * {@link #setPlaylist(List, MediaMetadata2)} depending on the {@link MediaPlaylistAgent}
-     * implementation. Use media items returned here for other playlist agent APIs such as
-     * {@link MediaPlaylistAgent#skipToPlaylistItem(MediaItem2)}.
-     *
-     * @return playlist
-     * @see MediaPlaylistAgent#getPlaylist()
-     * @see SessionCallback#onPlaylistChanged(
-     *          MediaSession2, MediaPlaylistAgent, List, MediaMetadata2)
-     */
-    public List<MediaItem2> getPlaylist() {
-        return mProvider.getPlaylist_impl();
-    }
-
-    /**
-     * Sets a list of {@link MediaItem2} to the {@link MediaPlaylistAgent}. Ensure uniqueness of
-     * each {@link MediaItem2} in the playlist so the session can uniquely identity individual
-     * items.
-     * <p>
-     * This may be an asynchronous call, and {@link MediaPlaylistAgent} may keep the copy of the
-     * list. Wait for {@link SessionCallback#onPlaylistChanged(MediaSession2, MediaPlaylistAgent,
-     * List, MediaMetadata2)} to know the operation finishes.
-     * <p>
-     * You may specify a {@link MediaItem2} without {@link DataSourceDesc}. In that case,
-     * {@link MediaPlaylistAgent} has responsibility to dynamically query {@link DataSourceDesc}
-     * when such media item is ready for preparation or play. Default implementation needs
-     * {@link OnDataSourceMissingHelper} for such case.
-     *
-     * @param list A list of {@link MediaItem2} objects to set as a play list.
-     * @throws IllegalArgumentException if given list is {@code null}, or has duplicated media
-     * items.
-     * @see MediaPlaylistAgent#setPlaylist(List, MediaMetadata2)
-     * @see SessionCallback#onPlaylistChanged(
-     *          MediaSession2, MediaPlaylistAgent, List, MediaMetadata2)
-     * @see #setOnDataSourceMissingHelper
-     */
-    public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
-        mProvider.setPlaylist_impl(list, metadata);
-    }
-
-    /**
-     * Skips to the item in the playlist.
-     * <p>
-     * This calls {@link MediaPlaylistAgent#skipToPlaylistItem(MediaItem2)} and the behavior depends
-     * on the playlist agent implementation, especially with the shuffle/repeat mode.
-     *
-     * @param item The item in the playlist you want to play
-     * @see #getShuffleMode()
-     * @see #getRepeatMode()
-     */
-    public void skipToPlaylistItem(@NonNull MediaItem2 item) {
-        mProvider.skipToPlaylistItem_impl(item);
-    }
-
-    /**
-     * Skips to the previous item.
-     * <p>
-     * This calls {@link MediaPlaylistAgent#skipToPreviousItem()} and the behavior depends on the
-     * playlist agent implementation, especially with the shuffle/repeat mode.
-     *
-     * @see #getShuffleMode()
-     * @see #getRepeatMode()
-     **/
-    public void skipToPreviousItem() {
-        mProvider.skipToPreviousItem_impl();
-    }
-
-    /**
-     * Skips to the next item.
-     * <p>
-     * This calls {@link MediaPlaylistAgent#skipToNextItem()} and the behavior depends on the
-     * playlist agent implementation, especially with the shuffle/repeat mode.
-     *
-     * @see #getShuffleMode()
-     * @see #getRepeatMode()
-     */
-    public void skipToNextItem() {
-        mProvider.skipToNextItem_impl();
-    }
-
-    /**
-     * Gets the playlist metadata from the {@link MediaPlaylistAgent}.
-     *
-     * @return the playlist metadata
-     */
-    public MediaMetadata2 getPlaylistMetadata() {
-        return mProvider.getPlaylistMetadata_impl();
-    }
-
-    /**
-     * Adds the media item to the playlist at position index. Index equals or greater than
-     * the current playlist size will add the item at the end of the playlist.
-     * <p>
-     * This will not change the currently playing media item.
-     * If index is less than or equal to the current index of the play list,
-     * the current index of the play list will be incremented correspondingly.
-     *
-     * @param index the index you want to add
-     * @param item the media item you want to add
-     */
-    public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
-        mProvider.addPlaylistItem_impl(index, item);
-    }
-
-    /**
-     * Removes the media item in the playlist.
-     * <p>
-     * If the item is the currently playing item of the playlist, current playback
-     * will be stopped and playback moves to next source in the list.
-     *
-     * @param item the media item you want to add
-     */
-    public void removePlaylistItem(@NonNull MediaItem2 item) {
-        mProvider.removePlaylistItem_impl(item);
-    }
-
-    /**
-     * Replaces the media item at index in the playlist. This can be also used to update metadata of
-     * an item.
-     *
-     * @param index the index of the item to replace
-     * @param item the new item
-     */
-    public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
-        mProvider.replacePlaylistItem_impl(index, item);
-    }
-
-    /**
-     * Return currently playing media item.
-     *
-     * @return currently playing media item
-     */
-    public MediaItem2 getCurrentMediaItem() {
-        // TODO(jaewan): Rename provider, and implement (b/74316764)
-        return mProvider.getCurrentPlaylistItem_impl();
-    }
-
-    /**
-     * Updates the playlist metadata to the {@link MediaPlaylistAgent}.
-     *
-     * @param metadata metadata of the playlist
-     */
-    public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
-        mProvider.updatePlaylistMetadata_impl(metadata);
-    }
-
-    /**
-     * Gets the repeat mode from the {@link MediaPlaylistAgent}.
-     *
-     * @return repeat mode
-     * @see MediaPlaylistAgent#REPEAT_MODE_NONE
-     * @see MediaPlaylistAgent#REPEAT_MODE_ONE
-     * @see MediaPlaylistAgent#REPEAT_MODE_ALL
-     * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
-     */
-    public @RepeatMode int getRepeatMode() {
-        return mProvider.getRepeatMode_impl();
-    }
-
-    /**
-     * Sets the repeat mode to the {@link MediaPlaylistAgent}.
-     *
-     * @param repeatMode repeat mode
-     * @see MediaPlaylistAgent#REPEAT_MODE_NONE
-     * @see MediaPlaylistAgent#REPEAT_MODE_ONE
-     * @see MediaPlaylistAgent#REPEAT_MODE_ALL
-     * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
-     */
-    public void setRepeatMode(@RepeatMode int repeatMode) {
-        mProvider.setRepeatMode_impl(repeatMode);
-    }
-
-    /**
-     * Gets the shuffle mode from the {@link MediaPlaylistAgent}.
-     *
-     * @return The shuffle mode
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
-     */
-    public @ShuffleMode int getShuffleMode() {
-        return mProvider.getShuffleMode_impl();
-    }
-
-    /**
-     * Sets the shuffle mode to the {@link MediaPlaylistAgent}.
-     *
-     * @param shuffleMode The shuffle mode
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
-     * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
-     */
-    public void setShuffleMode(@ShuffleMode int shuffleMode) {
-        mProvider.setShuffleMode_impl(shuffleMode);
-    }
-}
diff --git a/media/java/android/media/Rating2.java b/media/java/android/media/Rating2.java
deleted file mode 100644
index 9213190..0000000
--- a/media/java/android/media/Rating2.java
+++ /dev/null
@@ -1,260 +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.annotation.IntDef;
-import android.media.update.ApiLoader;
-import android.media.update.Rating2Provider;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * @hide
- * A class to encapsulate rating information used as content metadata.
- * A rating is defined by its rating style (see {@link #RATING_HEART},
- * {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
- * {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may
- * be defined as "unrated"), both of which are defined when the rating instance is constructed
- * through one of the factory methods.
- */
-// New version of Rating with following change
-//   - Don't implement Parcelable for updatable support.
-public final class Rating2 {
-    /**
-     * @hide
-     */
-    @IntDef({RATING_NONE, RATING_HEART, RATING_THUMB_UP_DOWN, RATING_3_STARS, RATING_4_STARS,
-            RATING_5_STARS, RATING_PERCENTAGE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Style {}
-
-    /**
-     * @hide
-     */
-    @IntDef({RATING_3_STARS, RATING_4_STARS, RATING_5_STARS})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface StarStyle {}
-
-    /**
-     * Indicates a rating style is not supported. A Rating2 will never have this
-     * type, but can be used by other classes to indicate they do not support
-     * Rating2.
-     */
-    public final static int RATING_NONE = 0;
-
-    /**
-     * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to
-     * indicate the content referred to is a favorite (or not).
-     */
-    public final static int RATING_HEART = 1;
-
-    /**
-     * A rating style for "thumb up" vs "thumb down".
-     */
-    public final static int RATING_THUMB_UP_DOWN = 2;
-
-    /**
-     * A rating style with 0 to 3 stars.
-     */
-    public final static int RATING_3_STARS = 3;
-
-    /**
-     * A rating style with 0 to 4 stars.
-     */
-    public final static int RATING_4_STARS = 4;
-
-    /**
-     * A rating style with 0 to 5 stars.
-     */
-    public final static int RATING_5_STARS = 5;
-
-    /**
-     * A rating style expressed as a percentage.
-     */
-    public final static int RATING_PERCENTAGE = 6;
-
-    private final Rating2Provider mProvider;
-
-    /**
-     * @hide
-     */
-    public Rating2(@NonNull Rating2Provider provider) {
-        mProvider = provider;
-    }
-
-    @Override
-    public String toString() {
-        return mProvider.toString_impl();
-    }
-
-    /**
-     * @hide
-     */
-    public Rating2Provider getProvider() {
-        return mProvider;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return mProvider.equals_impl(obj);
-    }
-
-    @Override
-    public int hashCode() {
-        return mProvider.hashCode_impl();
-    }
-
-    /**
-     * Create an instance from bundle object, previoulsy created by {@link #toBundle()}
-     *
-     * @param bundle bundle
-     * @return new Rating2 instance or {@code null} for error
-     */
-    public static Rating2 fromBundle(@Nullable Bundle bundle) {
-        return ApiLoader.getProvider().fromBundle_Rating2(bundle);
-    }
-
-    /**
-     * Return bundle for this object to share across the process.
-     * @return bundle of this object
-     */
-    public Bundle toBundle() {
-        return mProvider.toBundle_impl();
-    }
-
-    /**
-     * Return a Rating2 instance with no rating.
-     * Create and return a new Rating2 instance with no rating known for the given
-     * rating style.
-     * @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
-     *    {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
-     *    or {@link #RATING_PERCENTAGE}.
-     * @return null if an invalid rating style is passed, a new Rating2 instance otherwise.
-     */
-    public static @Nullable Rating2 newUnratedRating(@Style int ratingStyle) {
-        return ApiLoader.getProvider().newUnratedRating_Rating2(ratingStyle);
-    }
-
-    /**
-     * Return a Rating2 instance with a heart-based rating.
-     * Create and return a new Rating2 instance with a rating style of {@link #RATING_HEART},
-     * and a heart-based rating.
-     * @param hasHeart true for a "heart selected" rating, false for "heart unselected".
-     * @return a new Rating2 instance.
-     */
-    public static @Nullable Rating2 newHeartRating(boolean hasHeart) {
-        return ApiLoader.getProvider().newHeartRating_Rating2(hasHeart);
-    }
-
-    /**
-     * Return a Rating2 instance with a thumb-based rating.
-     * Create and return a new Rating2 instance with a {@link #RATING_THUMB_UP_DOWN}
-     * rating style, and a "thumb up" or "thumb down" rating.
-     * @param thumbIsUp true for a "thumb up" rating, false for "thumb down".
-     * @return a new Rating2 instance.
-     */
-    public static @Nullable Rating2 newThumbRating(boolean thumbIsUp) {
-        return ApiLoader.getProvider().newThumbRating_Rating2(thumbIsUp);
-    }
-
-    /**
-     * Return a Rating2 instance with a star-based rating.
-     * Create and return a new Rating2 instance with one of the star-base rating styles
-     * and the given integer or fractional number of stars. Non integer values can for instance
-     * be used to represent an average rating value, which might not be an integer number of stars.
-     * @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
-     *     {@link #RATING_5_STARS}.
-     * @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to
-     *     the rating style.
-     * @return null if the rating style is invalid, or the rating is out of range,
-     *     a new Rating2 instance otherwise.
-     */
-    public static @Nullable Rating2 newStarRating(
-            @StarStyle int starRatingStyle, float starRating) {
-        return ApiLoader.getProvider().newStarRating_Rating2(starRatingStyle, starRating);
-    }
-
-    /**
-     * Return a Rating2 instance with a percentage-based rating.
-     * Create and return a new Rating2 instance with a {@link #RATING_PERCENTAGE}
-     * rating style, and a rating of the given percentage.
-     * @param percent the value of the rating
-     * @return null if the rating is out of range, a new Rating2 instance otherwise.
-     */
-    public static @Nullable Rating2 newPercentageRating(float percent) {
-        return ApiLoader.getProvider().newPercentageRating_Rating2(percent);
-    }
-
-    /**
-     * Return whether there is a rating value available.
-     * @return true if the instance was not created with {@link #newUnratedRating(int)}.
-     */
-    public boolean isRated() {
-        return mProvider.isRated_impl();
-    }
-
-    /**
-     * Return the rating style.
-     * @return one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
-     *    {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
-     *    or {@link #RATING_PERCENTAGE}.
-     */
-    public @Style int getRatingStyle() {
-        return mProvider.getRatingStyle_impl();
-    }
-
-    /**
-     * Return whether the rating is "heart selected".
-     * @return true if the rating is "heart selected", false if the rating is "heart unselected",
-     *    if the rating style is not {@link #RATING_HEART} or if it is unrated.
-     */
-    public boolean hasHeart() {
-        return mProvider.hasHeart_impl();
-    }
-
-    /**
-     * Return whether the rating is "thumb up".
-     * @return true if the rating is "thumb up", false if the rating is "thumb down",
-     *    if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated.
-     */
-    public boolean isThumbUp() {
-        return mProvider.isThumbUp_impl();
-    }
-
-    /**
-     * Return the star-based rating value.
-     * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
-     *    not star-based, or if it is unrated.
-     */
-    public float getStarRating() {
-        return mProvider.getStarRating_impl();
-    }
-
-    /**
-     * Return the percentage-based rating value.
-     * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
-     *    not percentage-based, or if it is unrated.
-     */
-    public float getPercentRating() {
-        return mProvider.getPercentRating_impl();
-    }
-}
diff --git a/media/java/android/media/SessionCommand2.java b/media/java/android/media/SessionCommand2.java
deleted file mode 100644
index fe86a3a..0000000
--- a/media/java/android/media/SessionCommand2.java
+++ /dev/null
@@ -1,336 +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.content.Context;
-import android.media.update.ApiLoader;
-import android.media.update.MediaSession2Provider;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
-import android.net.Uri;
-import android.os.Bundle;
-
-import java.util.List;
-
-/**
- * @hide
- * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
- * <p>
- * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
- * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
- * {@link #getCustomCommand()} shouldn't be {@code null}.
- */
-public final class SessionCommand2 {
-    /**
-     * Command code for the custom command which can be defined by string action in the
-     * {@link SessionCommand2}.
-     */
-    public static final int COMMAND_CODE_CUSTOM = 0;
-
-    /**
-     * Command code for {@link MediaController2#play()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
-     * SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_PLAY = 1;
-
-    /**
-     * Command code for {@link MediaController2#pause()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
-     * SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
-
-    /**
-     * Command code for {@link MediaController2#stop()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
-     * SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
-
-    /**
-     * Command code for {@link MediaController2#skipToNextItem()}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the {@link SessionCallback#onCommandRequest(
-     * MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM = 4;
-
-    /**
-     * Command code for {@link MediaController2#skipToPreviousItem()}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the {@link SessionCallback#onCommandRequest(
-     * MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM = 5;
-
-    /**
-     * Command code for {@link MediaController2#prepare()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
-     * SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
-
-    /**
-     * Command code for {@link MediaController2#fastForward()}.
-     */
-    public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 7;
-
-    /**
-     * Command code for {@link MediaController2#rewind()}.
-     */
-    public static final int COMMAND_CODE_SESSION_REWIND = 8;
-
-    /**
-     * Command code for {@link MediaController2#seekTo(long)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
-     * SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
-
-    /**
-     * Command code for both {@link MediaController2#setVolumeTo(int, int)}.
-     * <p>
-     * Command would set the device volume or send to the volume provider directly if the session
-     * doesn't reject the request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_SET_VOLUME = 10;
-
-    /**
-     * Command code for both {@link MediaController2#adjustVolume(int, int)}.
-     * <p>
-     * Command would adjust the device volume or send to the volume provider directly if the session
-     * doesn't reject the request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_ADJUST_VOLUME = 11;
-
-    /**
-     * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12;
-
-    /**
-     * Command code for {@link MediaController2#setShuffleMode(int)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13;
-
-    /**
-     * Command code for {@link MediaController2#setRepeatMode(int)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14;
-
-    /**
-     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15;
-
-    /**
-     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16;
-
-    /**
-     * Command code for {@link MediaController2#replacePlaylistItem(int, MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17;
-
-    /**
-     * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
-     * information to the controller.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18;
-
-    /**
-     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19;
-
-    /**
-     * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose
-     * metadata information to the controller.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20;
-
-    /**
-     * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21;
-
-    /**
-     * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID = 22;
-
-    /**
-     * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PLAY_FROM_URI = 23;
-
-    /**
-     * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PLAY_FROM_SEARCH = 24;
-
-    /**
-     * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID = 25;
-
-    /**
-     * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_URI = 26;
-
-    /**
-     * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH = 27;
-
-    /**
-     * Command code for {@link MediaController2#setRating(String, Rating2)}.
-     */
-    public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
-
-    // TODO(jaewan): Add javadoc
-    public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;
-    public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 30;
-    public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 31;
-    public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 32;
-    public static final int COMMAND_CODE_LIBRARY_SEARCH = 33;
-    public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 34;
-    public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 35;
-
-    // TODO(jaewan): Rename and move provider
-    private final MediaSession2Provider.CommandProvider mProvider;
-
-    public SessionCommand2(int commandCode) {
-        mProvider = ApiLoader.getProvider().createMediaSession2Command(
-                this, commandCode, null, null);
-    }
-
-    public SessionCommand2(@NonNull String action, @Nullable Bundle extras) {
-        if (action == null) {
-            throw new IllegalArgumentException("action shouldn't be null");
-        }
-        mProvider = ApiLoader.getProvider().createMediaSession2Command(
-                this, COMMAND_CODE_CUSTOM, action, extras);
-    }
-
-    /**
-     * @hide
-     */
-    public MediaSession2Provider.CommandProvider getProvider() {
-        return mProvider;
-    }
-
-    public int getCommandCode() {
-        return mProvider.getCommandCode_impl();
-    }
-
-    public @Nullable String getCustomCommand() {
-        return mProvider.getCustomCommand_impl();
-    }
-
-    public @Nullable Bundle getExtras() {
-        return mProvider.getExtras_impl();
-    }
-
-    /**
-     * @return a new Bundle instance from the Command
-     * @hide
-     */
-    public Bundle toBundle() {
-        return mProvider.toBundle_impl();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof SessionCommand2)) {
-            return false;
-        }
-        return mProvider.equals_impl(((SessionCommand2) obj).mProvider);
-    }
-
-    @Override
-    public int hashCode() {
-        return mProvider.hashCode_impl();
-    }
-
-    /**
-     * @return a new Command instance from the Bundle
-     * @hide
-     */
-    public static SessionCommand2 fromBundle(@NonNull Bundle command) {
-        return ApiLoader.getProvider().fromBundle_MediaSession2Command(command);
-    }
-}
diff --git a/media/java/android/media/SessionCommandGroup2.java b/media/java/android/media/SessionCommandGroup2.java
deleted file mode 100644
index 399765e..0000000
--- a/media/java/android/media/SessionCommandGroup2.java
+++ /dev/null
@@ -1,106 +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.content.Context;
-import android.media.update.ApiLoader;
-import android.media.update.MediaSession2Provider;
-import android.os.Bundle;
-
-import java.util.Set;
-
-/**
- * @hide
- * Represent set of {@link SessionCommand2}.
- */
-public final class SessionCommandGroup2 {
-    // TODO(jaewan): Rename and move provider
-    private final MediaSession2Provider.CommandGroupProvider mProvider;
-
-    public SessionCommandGroup2() {
-        mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, null);
-    }
-
-    public SessionCommandGroup2(@Nullable SessionCommandGroup2 others) {
-        mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, others);
-    }
-
-    /**
-     * @hide
-     */
-    public SessionCommandGroup2(@NonNull MediaSession2Provider.CommandGroupProvider provider) {
-        mProvider = provider;
-    }
-
-    public void addCommand(@NonNull SessionCommand2 command) {
-        mProvider.addCommand_impl(command);
-    }
-
-    public void addCommand(int commandCode) {
-        // TODO(jaewna): Implement
-    }
-
-    public void addAllPredefinedCommands() {
-        mProvider.addAllPredefinedCommands_impl();
-    }
-
-    public void removeCommand(@NonNull SessionCommand2 command) {
-        mProvider.removeCommand_impl(command);
-    }
-
-    public void removeCommand(int commandCode) {
-        // TODO(jaewan): Implement.
-    }
-
-    public boolean hasCommand(@NonNull SessionCommand2 command) {
-        return mProvider.hasCommand_impl(command);
-    }
-
-    public boolean hasCommand(int code) {
-        return mProvider.hasCommand_impl(code);
-    }
-
-    public @NonNull
-    Set<SessionCommand2> getCommands() {
-        return mProvider.getCommands_impl();
-    }
-
-    /**
-     * @hide
-     */
-    public @NonNull MediaSession2Provider.CommandGroupProvider getProvider() {
-        return mProvider;
-    }
-
-    /**
-     * @return new bundle from the CommandGroup
-     * @hide
-     */
-    public @NonNull Bundle toBundle() {
-        return mProvider.toBundle_impl();
-    }
-
-    /**
-     * @return new instance of CommandGroup from the bundle
-     * @hide
-     */
-    public static @Nullable SessionCommandGroup2 fromBundle(Bundle commands) {
-        return ApiLoader.getProvider().fromBundle_MediaSession2CommandGroup(commands);
-    }
-}
diff --git a/media/java/android/media/SessionToken2.java b/media/java/android/media/SessionToken2.java
deleted file mode 100644
index f7d54f2..0000000
--- a/media/java/android/media/SessionToken2.java
+++ /dev/null
@@ -1,164 +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.content.Context;
-import android.media.session.MediaSessionManager;
-import android.media.update.ApiLoader;
-import android.media.update.SessionToken2Provider;
-import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * @hide
- * Represents an ongoing {@link MediaSession2}.
- * If it's representing a session service, it may not be ongoing.
- * <p>
- * This may be passed to apps by the session owner to allow them to create a
- * {@link MediaController2} to communicate with the session.
- * <p>
- * It can be also obtained by {@link MediaSessionManager}.
- */
-// New version of MediaSession.Token for following reasons
-//   - Stop implementing Parcelable for updatable support
-//   - Represent session and library service (formerly browser service) in one class.
-//     Previously MediaSession.Token was for session and ComponentName was for service.
-public final class SessionToken2 {
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
-    public @interface TokenType {
-    }
-
-    public static final int TYPE_SESSION = 0;
-    public static final int TYPE_SESSION_SERVICE = 1;
-    public static final int TYPE_LIBRARY_SERVICE = 2;
-
-    private final SessionToken2Provider mProvider;
-
-    // From the return value of android.os.Process.getUidForName(String) when error
-    private static final int UID_UNKNOWN = -1;
-
-    /**
-     * Constructor for the token. You can only create token for session service or library service
-     * to use by {@link MediaController2} or {@link MediaBrowser2}.
-     *
-     * @param context context
-     * @param packageName package name
-     * @param serviceName name of service. Can be {@code null} if it's not an service.
-     */
-    public SessionToken2(@NonNull Context context, @NonNull String packageName,
-            @NonNull String serviceName) {
-        this(context, packageName, serviceName, UID_UNKNOWN);
-    }
-
-    /**
-     * Constructor for the token. You can only create token for session service or library service
-     * to use by {@link MediaController2} or {@link MediaBrowser2}.
-     *
-     * @param context context
-     * @param packageName package name
-     * @param serviceName name of service. Can be {@code null} if it's not an service.
-     * @param uid uid of the app.
-     * @hide
-     */
-    public SessionToken2(@NonNull Context context, @NonNull String packageName,
-            @NonNull String serviceName, int uid) {
-        mProvider = ApiLoader.getProvider().createSessionToken2(
-                context, this, packageName, serviceName, uid);
-    }
-
-    /**
-     * Constructor for the token.
-     * @hide
-     */
-    public SessionToken2(@NonNull SessionToken2Provider provider) {
-        mProvider = provider;
-    }
-
-    @Override
-    public int hashCode() {
-        return mProvider.hashCode_impl();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return mProvider.equals_impl(obj);
-    }
-
-    @Override
-    public String toString() {
-        return mProvider.toString_impl();
-    }
-
-    /**
-     * @hide
-     */
-    public SessionToken2Provider getProvider() {
-        return mProvider;
-    }
-
-    /**
-     * @return uid of the session
-     */
-    public int getUid() {
-        return mProvider.getUid_impl();
-    }
-
-    /**
-     * @return package name
-     */
-    public String getPackageName() {
-        return mProvider.getPackageName_impl();
-    }
-
-    /**
-     * @return id
-     */
-    public String getId() {
-        return mProvider.getId_imp();
-    }
-
-    /**
-     * @return type of the token
-     * @see #TYPE_SESSION
-     * @see #TYPE_SESSION_SERVICE
-     */
-    public @TokenType int getType() {
-        return mProvider.getType_impl();
-    }
-
-    /**
-     * Create a token from the bundle, exported by {@link #toBundle()}.
-     * @param bundle
-     * @return
-     */
-    public static SessionToken2 fromBundle(@NonNull Bundle bundle) {
-        return ApiLoader.getProvider().fromBundle_SessionToken2(bundle);
-    }
-
-    /**
-     * Create a {@link Bundle} from this token to share it across processes.
-     * @return Bundle
-     */
-    public Bundle toBundle() {
-        return mProvider.toBundle_impl();
-    }
-}
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
deleted file mode 100644
index 1a4608f..0000000
--- a/media/java/android/media/VolumeProvider2.java
+++ /dev/null
@@ -1,147 +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.media.update.ApiLoader;
-import android.media.update.VolumeProvider2Provider;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * @hide
- * Handles requests to adjust or set the volume on a session. This is also used
- * to push volume updates back to the session. The provider must call
- * {@link #setCurrentVolume(int)} each time the volume being provided changes.
- * <p>
- * You can set a volume provider on a session by calling
- * {@link MediaSession2#updatePlayer}.
- */
-// New version of VolumeProvider with following changes
-//   - Don't implement Parcelable for updatable support.
-public abstract class VolumeProvider2 {
-    /**
-     * @hide
-     */
-    @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ControlType {}
-
-    /**
-     * The volume is fixed and can not be modified. Requests to change volume
-     * should be ignored.
-     */
-    public static final int VOLUME_CONTROL_FIXED = 0;
-
-    /**
-     * The volume control uses relative adjustment via
-     * {@link #onAdjustVolume(int)}. Attempts to set the volume to a specific
-     * value should be ignored.
-     */
-    public static final int VOLUME_CONTROL_RELATIVE = 1;
-
-    /**
-     * The volume control uses an absolute value. It may be adjusted using
-     * {@link #onAdjustVolume(int)} or set directly using
-     * {@link #onSetVolumeTo(int)}.
-     */
-    public static final int VOLUME_CONTROL_ABSOLUTE = 2;
-
-    private final VolumeProvider2Provider mProvider;
-
-    /**
-     * Create a new volume provider for handling volume events. You must specify
-     * the type of volume control, the maximum volume that can be used, and the
-     * current volume on the output.
-     *
-     * @param controlType The method for controlling volume that is used by this provider.
-     * @param maxVolume The maximum allowed volume.
-     * @param currentVolume The current volume on the output.
-     */
-    public VolumeProvider2(@ControlType int controlType, int maxVolume, int currentVolume) {
-        mProvider = ApiLoader.getProvider().createVolumeProvider2(
-                this, controlType, maxVolume, currentVolume);
-    }
-
-    /**
-     * @hide
-     */
-    public VolumeProvider2Provider getProvider() {
-        return mProvider;
-    }
-
-    /**
-     * Get the volume control type that this volume provider uses.
-     *
-     * @return The volume control type for this volume provider
-     */
-    @ControlType
-    public final int getControlType() {
-        return mProvider.getControlType_impl();
-    }
-
-    /**
-     * Get the maximum volume this provider allows.
-     *
-     * @return The max allowed volume.
-     */
-    public final int getMaxVolume() {
-        return mProvider.getMaxVolume_impl();
-    }
-
-    /**
-     * Gets the current volume. This will be the last value set by
-     * {@link #setCurrentVolume(int)}.
-     *
-     * @return The current volume.
-     */
-    public final int getCurrentVolume() {
-        return mProvider.getCurrentVolume_impl();
-    }
-
-    /**
-     * Notify the system that the current volume has been changed. This must be
-     * called every time the volume changes to ensure it is displayed properly.
-     *
-     * @param currentVolume The current volume on the output.
-     */
-    public final void setCurrentVolume(int currentVolume) {
-        mProvider.setCurrentVolume_impl(currentVolume);
-    }
-
-    /**
-     * Override to handle requests to set the volume of the current output.
-     * After the volume has been modified {@link #setCurrentVolume} must be
-     * called to notify the system.
-     *
-     * @param volume The volume to set the output to.
-     */
-    public void onSetVolumeTo(int volume) { }
-
-    /**
-     * Override to handle requests to adjust the volume of the current output.
-     * Direction will be one of {@link AudioManager#ADJUST_LOWER},
-     * {@link AudioManager#ADJUST_RAISE}, {@link AudioManager#ADJUST_SAME}.
-     * After the volume has been modified {@link #setCurrentVolume} must be
-     * called to notify the system.
-     *
-     * @param direction The direction to change the volume in.
-     */
-    public void onAdjustVolume(int direction) { }
-}
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 3578c16..4ced7be 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -17,7 +17,6 @@
 
 import android.content.ComponentName;
 import android.media.IRemoteVolumeController;
-import android.media.ISessionTokensListener;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -55,12 +54,4 @@
 
     // MediaSession2
     boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
-    boolean createSession2(in Bundle sessionToken);
-    void destroySession2(in Bundle sessionToken);
-    List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly,
-            String packageName);
-
-    void addSessionTokensListener(in ISessionTokensListener listener, int userId,
-            String packageName);
-    void removeSessionTokensListener(in ISessionTokensListener listener, String packageName);
 }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 98f3fb2..8215779 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -26,11 +26,7 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
-import android.media.ISessionTokensListener;
-import android.media.MediaSession2;
-import android.media.SessionToken2;
 import android.media.browse.MediaBrowser;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -44,10 +40,8 @@
 import android.view.KeyEvent;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.Executor;
 
 /**
  * Provides support for interacting with {@link MediaSession media sessions}
@@ -75,8 +69,6 @@
 
     private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners
             = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>();
-    private final ArrayMap<OnSessionTokensChangedListener, SessionTokensChangedWrapper>
-            mSessionTokensListener = new ArrayMap<>();
     private final Object mLock = new Object();
     private final ISessionManager mService;
 
@@ -413,73 +405,6 @@
     }
 
     /**
-     * Called when a {@link MediaSession2} is created.
-     * @hide
-     */
-    public boolean createSession2(@NonNull SessionToken2 token) {
-        if (token == null) {
-            return false;
-        }
-        try {
-            return mService.createSession2(token.toBundle());
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Cannot communicate with the service.", e);
-        }
-        return false;
-    }
-
-    /**
-     * Called when a {@link MediaSession2} is destroyed.
-     * @hide
-     */
-    public void destroySession2(@NonNull SessionToken2 token) {
-        if (token == null) {
-            return;
-        }
-        try {
-            mService.destroySession2(token.toBundle());
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Cannot communicate with the service.", e);
-        }
-    }
-
-    /**
-     * @hide
-     * Get {@link List} of {@link SessionToken2} whose sessions are active now. This list represents
-     * active sessions regardless of whether they're {@link MediaSession2}.
-     * <p>
-     * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
-     * calling app. You may also retrieve this list if your app is an enabled notification listener
-     * using the {@link NotificationListenerService} APIs.
-     *
-     * @return list of tokens
-     */
-    public List<SessionToken2> getActiveSessionTokens() {
-        try {
-            List<Bundle> bundles = mService.getSessionTokens(
-                    /* activeSessionOnly */ true, /* sessionServiceOnly */ false,
-                    mContext.getOpPackageName());
-            return toTokenList(bundles);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Cannot communicate with the service.", e);
-            return Collections.emptyList();
-        }
-    }
-
-    private static List<SessionToken2> toTokenList(List<Bundle> bundles) {
-        List<SessionToken2> tokens = new ArrayList<>();
-        if (bundles != null) {
-            for (int i = 0; i < bundles.size(); i++) {
-                SessionToken2 token = SessionToken2.fromBundle(bundles.get(i));
-                if (token != null) {
-                    tokens.add(token);
-                }
-            }
-        }
-        return tokens;
-    }
-
-    /**
      * Check if the global priority session is currently active. This can be
      * used to decide if media keys should be sent to the session or to the app.
      *
@@ -601,15 +526,6 @@
     }
 
     /**
-     * @hide
-     * Listens for changes to the {@link #getAllSessionTokens()}. This can be added
-     * using {@link #addOnActiveSessionsChangedListener}.
-     */
-    public interface OnSessionTokensChangedListener {
-        void onSessionTokensChanged(@NonNull List<SessionToken2> tokens);
-    }
-
-    /**
      * Listens the volume key long-presses.
      * @hide
      */
@@ -815,41 +731,6 @@
         }
     }
 
-    private static final class SessionTokensChangedWrapper {
-        private Context mContext;
-        private Executor mExecutor;
-        private OnSessionTokensChangedListener mListener;
-
-        public SessionTokensChangedWrapper(Context context, Executor executor,
-                OnSessionTokensChangedListener listener) {
-            mContext = context;
-            mExecutor = executor;
-            mListener = listener;
-        }
-
-        private final ISessionTokensListener.Stub mStub = new ISessionTokensListener.Stub() {
-            @Override
-            public void onSessionTokensChanged(final List<Bundle> bundles) {
-                final Executor executor = mExecutor;
-                if (executor != null) {
-                    executor.execute(() -> {
-                        final Context context = mContext;
-                        final OnSessionTokensChangedListener listener = mListener;
-                        if (context != null && listener != null) {
-                            listener.onSessionTokensChanged(toTokenList(bundles));
-                        }
-                    });
-                }
-            }
-        };
-
-        private void release() {
-            mListener = null;
-            mContext = null;
-            mExecutor = null;
-        }
-    }
-
     private static final class OnVolumeKeyLongPressListenerImpl
             extends IOnVolumeKeyLongPressListener.Stub {
         private OnVolumeKeyLongPressListener mListener;
diff --git a/media/java/android/media/update/ApiLoader.java b/media/java/android/media/update/ApiLoader.java
deleted file mode 100644
index 0c1d1a2..0000000
--- a/media/java/android/media/update/ApiLoader.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.update;
-
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Build;
-import android.os.RemoteException;
-import android.os.UserHandle;
-
-import com.android.internal.annotations.GuardedBy;
-
-import dalvik.system.PathClassLoader;
-
-import java.io.File;
-
-/**
- * @hide
- */
-public final class ApiLoader {
-    @GuardedBy("this")
-    private static StaticProvider sMediaUpdatable;
-
-    private static final String UPDATE_PACKAGE = "com.android.media.update";
-    private static final String UPDATE_CLASS = "com.android.media.update.ApiFactory";
-    private static final String UPDATE_METHOD = "initialize";
-    private static final boolean REGISTER_UPDATE_DEPENDENCY = true;
-
-    private ApiLoader() { }
-
-    public static StaticProvider getProvider() {
-        try {
-            return getMediaUpdatable();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (NameNotFoundException | ReflectiveOperationException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    // TODO This method may do I/O; Ensure it does not violate (emit warnings in) strict mode.
-    private static synchronized StaticProvider getMediaUpdatable()
-            throws NameNotFoundException, ReflectiveOperationException, RemoteException {
-        if (sMediaUpdatable != null) return sMediaUpdatable;
-
-        // TODO Figure out when to use which package (query media update service)
-        int flags = Build.IS_DEBUGGABLE ? 0 : PackageManager.MATCH_SYSTEM_ONLY;
-        ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
-                UPDATE_PACKAGE, flags, UserHandle.myUserId());
-
-        if (REGISTER_UPDATE_DEPENDENCY) {
-            // Register a dependency to the updatable in order to be killed during updates
-            ActivityManager.getService().addPackageDependency(ai.packageName);
-        }
-
-        ClassLoader classLoader = new PathClassLoader(ai.sourceDir,
-                ai.nativeLibraryDir + File.pathSeparator + System.getProperty("java.library.path"),
-                ClassLoader.getSystemClassLoader().getParent());
-        return sMediaUpdatable = (StaticProvider) classLoader.loadClass(UPDATE_CLASS)
-                .getMethod(UPDATE_METHOD, ApplicationInfo.class).invoke(null, ai);
-    }
-}
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
deleted file mode 100644
index 7234f7b..0000000
--- a/media/java/android/media/update/MediaController2Provider.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.update;
-
-import android.app.PendingIntent;
-import android.media.AudioAttributes;
-import android.media.MediaController2.PlaybackInfo;
-import android.media.MediaItem2;
-import android.media.MediaMetadata2;
-import android.media.SessionCommand2;
-import android.media.Rating2;
-import android.media.SessionToken2;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-import java.util.List;
-
-/**
- * @hide
- */
-public interface MediaController2Provider extends TransportControlProvider {
-    void initialize();
-
-    void close_impl();
-    SessionToken2 getSessionToken_impl();
-    boolean isConnected_impl();
-
-    PendingIntent getSessionActivity_impl();
-
-    void setVolumeTo_impl(int value, int flags);
-    void adjustVolume_impl(int direction, int flags);
-    PlaybackInfo getPlaybackInfo_impl();
-
-    void prepareFromUri_impl(Uri uri, Bundle extras);
-    void prepareFromSearch_impl(String query, Bundle extras);
-    void prepareFromMediaId_impl(String mediaId, Bundle extras);
-    void playFromSearch_impl(String query, Bundle extras);
-    void playFromUri_impl(Uri uri, Bundle extras);
-    void playFromMediaId_impl(String mediaId, Bundle extras);
-    void fastForward_impl();
-    void rewind_impl();
-
-    void setRating_impl(String mediaId, Rating2 rating);
-    void sendCustomCommand_impl(SessionCommand2 command, Bundle args, ResultReceiver cb);
-    List<MediaItem2> getPlaylist_impl();
-    void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
-    MediaMetadata2 getPlaylistMetadata_impl();
-    void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
-
-    void addPlaylistItem_impl(int index, MediaItem2 item);
-    void replacePlaylistItem_impl(int index, MediaItem2 item);
-    void removePlaylistItem_impl(MediaItem2 item);
-
-    int getPlayerState_impl();
-    long getCurrentPosition_impl();
-    float getPlaybackSpeed_impl();
-    long getBufferedPosition_impl();
-    MediaItem2 getCurrentMediaItem_impl();
-
-    interface PlaybackInfoProvider {
-        int getPlaybackType_impl();
-        AudioAttributes getAudioAttributes_impl();
-        int getControlType_impl();
-        int getMaxVolume_impl();
-        int getCurrentVolume_impl();
-    }
-}
diff --git a/media/java/android/media/update/MediaItem2Provider.java b/media/java/android/media/update/MediaItem2Provider.java
deleted file mode 100644
index 47db22f..0000000
--- a/media/java/android/media/update/MediaItem2Provider.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.update;
-
-import android.media.DataSourceDesc;
-import android.media.MediaItem2;
-import android.media.MediaItem2.Builder;
-import android.media.MediaMetadata2;
-import android.os.Bundle;
-
-/**
- * @hide
- */
-public interface MediaItem2Provider {
-    Bundle toBundle_impl();
-    String toString_impl();
-    int getFlags_impl();
-    boolean isBrowsable_impl();
-    boolean isPlayable_impl();
-    void setMetadata_impl(MediaMetadata2 metadata);
-    MediaMetadata2 getMetadata_impl();
-    String getMediaId_impl();
-    DataSourceDesc getDataSourceDesc_impl();
-    boolean equals_impl(Object obj);
-
-    interface BuilderProvider {
-        Builder setMediaId_impl(String mediaId);
-        Builder setMetadata_impl(MediaMetadata2 metadata);
-        Builder setDataSourceDesc_impl(DataSourceDesc dataSourceDesc);
-        MediaItem2 build_impl();
-    }
-}
diff --git a/media/java/android/media/update/MediaMetadata2Provider.java b/media/java/android/media/update/MediaMetadata2Provider.java
deleted file mode 100644
index 22463e9..0000000
--- a/media/java/android/media/update/MediaMetadata2Provider.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package android.media.update;
-
-import android.graphics.Bitmap;
-import android.media.MediaMetadata2;
-import android.media.MediaMetadata2.Builder;
-import android.media.Rating2;
-import android.os.Bundle;
-
-import java.util.Set;
-
-/**
- * @hide
- */
-public interface MediaMetadata2Provider {
-    boolean containsKey_impl(String key);
-    CharSequence getText_impl(String key);
-    String getMediaId_impl();
-    String getString_impl(String key);
-    long getLong_impl(String key);
-    Rating2 getRating_impl(String key);
-    Bundle toBundle_impl();
-    Set<String> keySet_impl();
-    int size_impl();
-    Bitmap getBitmap_impl(String key);
-    float getFloat_impl(String key);
-    Bundle getExtras_impl();
-
-    interface BuilderProvider {
-        Builder putText_impl(String key, CharSequence value);
-        Builder putString_impl(String key, String value);
-        Builder putLong_impl(String key, long value);
-        Builder putRating_impl(String key, Rating2 value);
-        Builder putBitmap_impl(String key, Bitmap value);
-        Builder putFloat_impl(String key, float value);
-        Builder setExtras_impl(Bundle bundle);
-        MediaMetadata2 build_impl();
-    }
-}
diff --git a/media/java/android/media/update/MediaPlaylistAgentProvider.java b/media/java/android/media/update/MediaPlaylistAgentProvider.java
deleted file mode 100644
index e1522cf..0000000
--- a/media/java/android/media/update/MediaPlaylistAgentProvider.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.update;
-
-import android.media.DataSourceDesc;
-import android.media.MediaItem2;
-import android.media.MediaMetadata2;
-import android.media.MediaPlaylistAgent.PlaylistEventCallback;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- */
-public interface MediaPlaylistAgentProvider {
-    // final methods of MediaPlaylistAgent
-    void registerPlaylistEventCallback_impl(Executor executor, PlaylistEventCallback callback);
-    void unregisterPlaylistEventCallback_impl(PlaylistEventCallback callback);
-    void notifyPlaylistChanged_impl();
-    void notifyPlaylistMetadataChanged_impl();
-    void notifyShuffleModeChanged_impl();
-    void notifyRepeatModeChanged_impl();
-
-    // public methods of MediaPlaylistAgent
-    List<MediaItem2> getPlaylist_impl();
-    void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
-    MediaMetadata2 getPlaylistMetadata_impl();
-    void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
-    void addPlaylistItem_impl(int index, MediaItem2 item);
-    void removePlaylistItem_impl(MediaItem2 item);
-    void replacePlaylistItem_impl(int index, MediaItem2 item);
-    void skipToPlaylistItem_impl(MediaItem2 item);
-    void skipToPreviousItem_impl();
-    void skipToNextItem_impl();
-    int getRepeatMode_impl();
-    void setRepeatMode_impl(int repeatMode);
-    int getShuffleMode_impl();
-    void setShuffleMode_impl(int shuffleMode);
-    MediaItem2 getMediaItem_impl(DataSourceDesc dsd);
-}
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
deleted file mode 100644
index 4751348..0000000
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.update;
-
-import android.app.PendingIntent;
-import android.media.AudioFocusRequest;
-import android.media.MediaItem2;
-import android.media.MediaMetadata2;
-import android.media.MediaPlayerBase;
-import android.media.MediaPlaylistAgent;
-import android.media.MediaSession2;
-import android.media.SessionCommand2;
-import android.media.MediaSession2.CommandButton;
-import android.media.MediaSession2.CommandButton.Builder;
-import android.media.SessionCommandGroup2;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.OnDataSourceMissingHelper;
-import android.media.MediaSession2.SessionCallback;
-import android.media.SessionToken2;
-import android.media.VolumeProvider2;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- */
-public interface MediaSession2Provider extends TransportControlProvider {
-    void close_impl();
-    void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistAgent playlistAgent,
-            VolumeProvider2 volumeProvider);
-    MediaPlayerBase getPlayer_impl();
-    MediaMetadata2 getPlaylistMetadata_impl();
-    void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
-    MediaPlaylistAgent getPlaylistAgent_impl();
-    VolumeProvider2 getVolumeProvider_impl();
-    SessionToken2 getToken_impl();
-    List<ControllerInfo> getConnectedControllers_impl();
-    void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout);
-    void setAudioFocusRequest_impl(AudioFocusRequest afr);
-    void setAllowedCommands_impl(ControllerInfo controller, SessionCommandGroup2 commands);
-    void sendCustomCommand_impl(ControllerInfo controller, SessionCommand2 command, Bundle args,
-            ResultReceiver receiver);
-    void sendCustomCommand_impl(SessionCommand2 command, Bundle args);
-    void addPlaylistItem_impl(int index, MediaItem2 item);
-    void removePlaylistItem_impl(MediaItem2 item);
-    void replacePlaylistItem_impl(int index, MediaItem2 item);
-    List<MediaItem2> getPlaylist_impl();
-    void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
-    MediaItem2 getCurrentPlaylistItem_impl();
-    void notifyError_impl(int errorCode, Bundle extras);
-    int getPlayerState_impl();
-    long getCurrentPosition_impl();
-    long getBufferedPosition_impl();
-    void setOnDataSourceMissingHelper_impl(OnDataSourceMissingHelper helper);
-    void clearOnDataSourceMissingHelper_impl();
-
-    // TODO(jaewan): Rename and move provider
-    interface CommandProvider {
-        int getCommandCode_impl();
-        String getCustomCommand_impl();
-        Bundle getExtras_impl();
-        Bundle toBundle_impl();
-
-        boolean equals_impl(Object ob);
-        int hashCode_impl();
-    }
-
-    // TODO(jaewan): Rename and move provider
-    interface CommandGroupProvider {
-        void addCommand_impl(SessionCommand2 command);
-        void addAllPredefinedCommands_impl();
-        void removeCommand_impl(SessionCommand2 command);
-        boolean hasCommand_impl(SessionCommand2 command);
-        boolean hasCommand_impl(int code);
-        Set<SessionCommand2> getCommands_impl();
-        Bundle toBundle_impl();
-    }
-
-    interface CommandButtonProvider {
-        SessionCommand2 getCommand_impl();
-        int getIconResId_impl();
-        String getDisplayName_impl();
-        Bundle getExtras_impl();
-        boolean isEnabled_impl();
-
-        interface BuilderProvider {
-            Builder setCommand_impl(SessionCommand2 command);
-            Builder setIconResId_impl(int resId);
-            Builder setDisplayName_impl(String displayName);
-            Builder setEnabled_impl(boolean enabled);
-            Builder setExtras_impl(Bundle extras);
-            CommandButton build_impl();
-        }
-    }
-
-    interface ControllerInfoProvider {
-        String getPackageName_impl();
-        int getUid_impl();
-        boolean isTrusted_impl();
-        int hashCode_impl();
-        boolean equals_impl(Object obj);
-        String toString_impl();
-    }
-
-    interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> {
-        void setPlayer_impl(MediaPlayerBase player);
-        void setPlaylistAgent_impl(MediaPlaylistAgent playlistAgent);
-        void setVolumeProvider_impl(VolumeProvider2 volumeProvider);
-        void setSessionActivity_impl(PendingIntent pi);
-        void setId_impl(String id);
-        void setSessionCallback_impl(Executor executor, C callback);
-        T build_impl();
-    }
-}
diff --git a/media/java/android/media/update/Rating2Provider.java b/media/java/android/media/update/Rating2Provider.java
deleted file mode 100644
index 28ad273..0000000
--- a/media/java/android/media/update/Rating2Provider.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.update;
-
-import android.annotation.SystemApi;
-import android.os.Bundle;
-
-/**
- * @hide
- */
-public interface Rating2Provider {
-    String toString_impl();
-    boolean equals_impl(Object obj);
-    int hashCode_impl();
-    Bundle toBundle_impl();
-    boolean isRated_impl();
-    int getRatingStyle_impl();
-    boolean hasHeart_impl();
-    boolean isThumbUp_impl();
-    float getStarRating_impl();
-    float getPercentRating_impl();
-}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
deleted file mode 100644
index ccb8c02..0000000
--- a/media/java/android/media/update/StaticProvider.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.update;
-
-import android.content.Context;
-import android.media.MediaController2;
-import android.media.MediaController2.ControllerCallback;
-import android.media.MediaItem2;
-import android.media.MediaMetadata2;
-import android.media.MediaPlaylistAgent;
-import android.media.MediaSession2;
-import android.media.MediaSession2.SessionCallback;
-import android.media.Rating2;
-import android.media.SessionCommand2;
-import android.media.SessionCommandGroup2;
-import android.media.SessionToken2;
-import android.media.VolumeProvider2;
-import android.media.update.MediaSession2Provider.BuilderBaseProvider;
-import android.media.update.MediaSession2Provider.CommandButtonProvider;
-import android.media.update.MediaSession2Provider.CommandGroupProvider;
-import android.media.update.MediaSession2Provider.CommandProvider;
-import android.media.update.MediaSession2Provider.ControllerInfoProvider;
-import android.os.Bundle;
-import android.os.IInterface;
-
-import java.util.concurrent.Executor;
-
-/**
- * Interface for connecting the public API to an updatable implementation.
- *
- * This interface provides access to constructors and static methods that are otherwise not directly
- * accessible via an implementation object.
- * @hide
- */
-public interface StaticProvider {
-    CommandProvider createMediaSession2Command(SessionCommand2 instance,
-            int commandCode, String action, Bundle extra);
-    SessionCommand2 fromBundle_MediaSession2Command(Bundle bundle);
-    CommandGroupProvider createMediaSession2CommandGroup(SessionCommandGroup2 instance,
-            SessionCommandGroup2 others);
-    SessionCommandGroup2 fromBundle_MediaSession2CommandGroup(Bundle bundle);
-    ControllerInfoProvider createMediaSession2ControllerInfo(Context context,
-            MediaSession2.ControllerInfo instance, int uid, int pid,
-            String packageName, IInterface callback);
-    CommandButtonProvider.BuilderProvider createMediaSession2CommandButtonBuilder(
-            MediaSession2.CommandButton.Builder instance);
-    BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
-            Context context, MediaSession2.Builder instance);
-
-    MediaController2Provider createMediaController2(Context context, MediaController2 instance,
-            SessionToken2 token, Executor executor, ControllerCallback callback);
-
-    SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
-            String packageName, String serviceName, int uid);
-    SessionToken2 fromBundle_SessionToken2(Bundle bundle);
-
-    MediaItem2Provider.BuilderProvider createMediaItem2Builder(MediaItem2.Builder instance,
-            int flags);
-    MediaItem2 fromBundle_MediaItem2(Bundle bundle);
-
-    VolumeProvider2Provider createVolumeProvider2(VolumeProvider2 instance, int controlType,
-            int maxVolume, int currentVolume);
-
-    MediaMetadata2 fromBundle_MediaMetadata2(Bundle bundle);
-    MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
-            MediaMetadata2.Builder instance);
-    MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
-            MediaMetadata2.Builder instance, MediaMetadata2 source);
-
-    Rating2 newUnratedRating_Rating2(int ratingStyle);
-    Rating2 fromBundle_Rating2(Bundle bundle);
-    Rating2 newHeartRating_Rating2(boolean hasHeart);
-    Rating2 newThumbRating_Rating2(boolean thumbIsUp);
-    Rating2 newStarRating_Rating2(int starRatingStyle, float starRating);
-    Rating2 newPercentageRating_Rating2(float percent);
-
-    MediaPlaylistAgentProvider createMediaPlaylistAgent(MediaPlaylistAgent instance);
-}
diff --git a/media/java/android/media/update/TransportControlProvider.java b/media/java/android/media/update/TransportControlProvider.java
deleted file mode 100644
index d89a88a..0000000
--- a/media/java/android/media/update/TransportControlProvider.java
+++ /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.update;
-
-import android.media.MediaItem2;
-
-/**
- * @hide
- */
-public interface TransportControlProvider {
-    void play_impl();
-    void pause_impl();
-    void stop_impl();
-    void skipToPreviousItem_impl();
-    void skipToNextItem_impl();
-
-    void prepare_impl();
-    void seekTo_impl(long pos);
-    void skipToPlaylistItem_impl(MediaItem2 item);
-
-    int getRepeatMode_impl();
-    void setRepeatMode_impl(int repeatMode);
-    int getShuffleMode_impl();
-    void setShuffleMode_impl(int shuffleMode);
-}
diff --git a/media/java/android/media/update/VolumeProvider2Provider.java b/media/java/android/media/update/VolumeProvider2Provider.java
deleted file mode 100644
index 5b5cfd3..0000000
--- a/media/java/android/media/update/VolumeProvider2Provider.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.update;
-
-/**
- * @hide
- */
-public interface VolumeProvider2Provider {
-    int getControlType_impl();
-    int getMaxVolume_impl();
-    int getCurrentVolume_impl();
-    void setCurrentVolume_impl(int currentVolume);
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 8fac3fd..99d48d3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -34,7 +34,7 @@
 
 import com.android.settingslib.R;
 
-import libcore.util.TimeZoneFinder;
+import libcore.timezone.TimeZoneFinder;
 
 import org.xmlpull.v1.XmlPullParserException;
 
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 75c0391..6ba1fcb 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -1,10 +1,12 @@
+set noparent
+
+jsharkey@android.com
+felipeal@google.com
+nandana@google.com
 svetoslavganov@google.com
 hackbod@google.com
 yamasani@google.com
 moltmann@google.com
 toddke@google.com
-jsharkey@google.com
 cbrubaker@google.com
 omakoto@google.com
-nandana@google.com
-felipeal@google.com
diff --git a/packages/SystemUI/res/layout/smart_action_button.xml b/packages/SystemUI/res/layout/smart_action_button.xml
new file mode 100644
index 0000000..2716034
--- /dev/null
+++ b/packages/SystemUI/res/layout/smart_action_button.xml
@@ -0,0 +1,34 @@
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<!-- android:paddingHorizontal is set dynamically in SmartReplyView. -->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+        style="@android:style/Widget.Material.Button"
+        android:stateListAnimator="@null"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="0dp"
+        android:minHeight="@dimen/smart_reply_button_min_height"
+        android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
+        android:background="@drawable/smart_reply_button_background"
+        android:gravity="center"
+        android:fontFamily="roboto-medium"
+        android:textSize="@dimen/smart_reply_button_font_size"
+        android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
+        android:textColor="@color/smart_reply_button_text"
+        android:drawablePadding="@dimen/smart_action_button_icon_padding"
+        android:textStyle="normal"
+        android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 07628c6..0997c5b1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -881,6 +881,7 @@
     <dimen name="smart_reply_button_stroke_width">1dp</dimen>
     <dimen name="smart_reply_button_font_size">14sp</dimen>
     <dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
+    <dimen name="smart_action_button_icon_padding">10dp</dimen>
 
     <!-- A reasonable upper bound for the height of the smart reply button. The measuring code
             needs to start with a guess for the maximum size. Currently two-line smart reply buttons
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index c7910f9..46ed715b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -54,7 +54,6 @@
 import android.util.Log;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
-
 import android.view.RemoteAnimationTarget;
 
 import com.android.internal.app.IVoiceInteractionManagerService;
@@ -480,4 +479,16 @@
             return false;
         }
     }
+
+    /**
+     * Returns true if the system supports freeform multi-window.
+     */
+    public boolean supportsFreeformMultiWindow(Context context) {
+        final boolean freeformDevOption = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+        return ActivityTaskManager.supportsMultiWindow(context)
+                && (context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+                || freeformDevOption);
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 7154f53..a6b66e7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 
 import android.app.ActivityOptions;
@@ -41,6 +42,15 @@
         return options;
     }
 
+    /**
+     * @return ActivityOptions for starting a task in freeform.
+     */
+    public static ActivityOptions makeFreeformOptions() {
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+        return options;
+    }
+
     public static ActivityOptions makeRemoteAnimation(
             RemoteAnimationAdapterCompat remoteAnimationAdapter) {
         return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped());
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 3191d14..42e60aa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -17,6 +17,9 @@
 package com.android.systemui.shared.system;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
 
 import android.app.WindowConfiguration;
 import android.graphics.Rect;
@@ -26,10 +29,6 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
 import com.android.systemui.shared.recents.view.RecentsTransition;
 
@@ -179,6 +178,7 @@
      */
     public int getNavBarPosition() {
         try {
+            // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
             return WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to get nav bar position");
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 35ae899..5d33ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -90,7 +90,7 @@
     private float[] mRecentTemps = new float[MAX_RECENT_TEMPS];
     private int mNumTemps;
     private long mNextLogTime;
-    private IThermalService mThermalService;
+    @VisibleForTesting IThermalService mThermalService;
 
     @VisibleForTesting int mBatteryLevel = 100;
     @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
@@ -394,7 +394,7 @@
             // Enable push notifications of throttling from vendor thermal
             // management subsystem via thermalservice, in addition to our
             // usual polling, to react to temperature jumps more quickly.
-            IBinder b = ServiceManager.getService("thermalservice");
+            IBinder b = ServiceManager.getService(Context.THERMAL_SERVICE);
 
             if (b != null) {
                 mThermalService = IThermalService.Stub.asInterface(b);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index fa3fa5b..bb9a341 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -55,6 +55,8 @@
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.SmartReplyView;
 
+import java.util.List;
+
 /**
  * A frame layout containing the actual payload of the notification, including the contracted,
  * expanded and heads up layout. This class is responsible for clipping the content and and
@@ -1285,38 +1287,88 @@
             return;
         }
 
-        Notification notification = entry.notification.getNotification();
+        SmartRepliesAndActions smartRepliesAndActions = chooseSmartRepliesAndActions(
+                mSmartReplyConstants, entry);
 
-        Pair<RemoteInput, Notification.Action> remoteInputActionPair =
-                entry.notification.getNotification().findRemoteInputActionPair(false /*freeform */);
-        Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
-                notification.findRemoteInputActionPair(true /*freeform */);
+        applyRemoteInput(entry, smartRepliesAndActions.freeformRemoteInputActionPair != null);
+        applySmartReplyView(smartRepliesAndActions, entry);
+    }
 
-        boolean enableAppGeneratedSmartReplies = (mSmartReplyConstants.isEnabled()
-                && (!mSmartReplyConstants.requiresTargetingP()
+    /**
+     * Chose what smart replies and smart actions to display. App generated suggestions take
+     * precedence. So if the app provides any smart replies, we don't show any
+     * replies or actions generated by the NotificationAssistantService (NAS), and if the app
+     * provides any smart actions we also don't show any NAS-generated replies or actions.
+     */
+    @VisibleForTesting
+    static SmartRepliesAndActions chooseSmartRepliesAndActions(
+            SmartReplyConstants smartReplyConstants,
+            final NotificationData.Entry entry) {
+        boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
+                && (!smartReplyConstants.requiresTargetingP()
                 || entry.targetSdk >= Build.VERSION_CODES.P));
 
-        RemoteInput remoteInputWithChoices = null;
-        PendingIntent pendingIntentWithChoices= null;
-        CharSequence[] choices = null;
-        if (enableAppGeneratedSmartReplies
-                && remoteInputActionPair != null
-                && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())) {
-            // app generated smart replies
-            remoteInputWithChoices = remoteInputActionPair.first;
-            pendingIntentWithChoices = remoteInputActionPair.second.actionIntent;
-            choices = remoteInputActionPair.first.getChoices();
+        Notification notification = entry.notification.getNotification();
+        Pair<RemoteInput, Notification.Action> remoteInputActionPair =
+                notification.findRemoteInputActionPair(false /* freeform */);
+        Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
+                notification.findRemoteInputActionPair(true /* freeform */);
+
+        boolean appGeneratedSmartRepliesExist =
+                enableAppGeneratedSmartReplies
+                        && remoteInputActionPair != null
+                        && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices());
+
+        List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
+        boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
+
+        if (appGeneratedSmartRepliesExist) {
+            return new SmartRepliesAndActions(remoteInputActionPair.first,
+                    remoteInputActionPair.second.actionIntent,
+                    remoteInputActionPair.first.getChoices(),
+                    appGeneratedSmartActions,
+                    freeformRemoteInputActionPair);
+        } else if (appGeneratedSmartActionsExist) {
+            return new SmartRepliesAndActions(null, null, null, appGeneratedSmartActions,
+                    freeformRemoteInputActionPair);
         } else if (!ArrayUtils.isEmpty(entry.smartReplies)
                 && freeformRemoteInputActionPair != null
                 && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()) {
-            // system generated smart replies
-            remoteInputWithChoices = freeformRemoteInputActionPair.first;
-            pendingIntentWithChoices = freeformRemoteInputActionPair.second.actionIntent;
-            choices = entry.smartReplies;
+            // App didn't generate anything, use NAS-generated replies and actions
+            return new SmartRepliesAndActions(freeformRemoteInputActionPair.first,
+                    freeformRemoteInputActionPair.second.actionIntent,
+                    entry.smartReplies,
+                    entry.systemGeneratedSmartActions,
+                    freeformRemoteInputActionPair);
+        }
+        // App didn't generate anything, and there are no NAS-generated smart replies.
+        return new SmartRepliesAndActions(null, null, null, entry.systemGeneratedSmartActions,
+                freeformRemoteInputActionPair);
+    }
+
+    @VisibleForTesting
+    static class SmartRepliesAndActions {
+        public final RemoteInput remoteInputWithChoices;
+        public final PendingIntent pendingIntentForSmartReplies;
+        public final CharSequence[] smartReplies;
+        public final List<Notification.Action> smartActions;
+        public final Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair;
+
+        SmartRepliesAndActions(RemoteInput remoteInput, PendingIntent pendingIntent,
+                CharSequence[] choices, List<Notification.Action> smartActions,
+                Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair) {
+            this.remoteInputWithChoices = remoteInput;
+            this.pendingIntentForSmartReplies = pendingIntent;
+            this.smartReplies = choices;
+            this.smartActions = smartActions;
+            this.freeformRemoteInputActionPair = freeformRemoteInputActionPair;
         }
 
-        applyRemoteInput(entry, freeformRemoteInputActionPair != null);
-        applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry, choices);
+        boolean smartRepliesExist() {
+            return remoteInputWithChoices != null
+                    && pendingIntentForSmartReplies != null
+                    && !ArrayUtils.isEmpty(smartReplies);
+        }
     }
 
     private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
@@ -1418,28 +1470,32 @@
         return null;
     }
 
-    private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
-            NotificationData.Entry entry, CharSequence[] choices) {
+    private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions,
+            NotificationData.Entry entry) {
         if (mExpandedChild != null) {
             mExpandedSmartReplyView =
-                    applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry, choices);
-            if (mExpandedSmartReplyView != null && remoteInput != null
-                    && choices != null && choices.length > 0) {
-                mSmartReplyController.smartRepliesAdded(entry, choices.length);
+                    applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
+            if (mExpandedSmartReplyView != null
+                    && smartRepliesAndActions.remoteInputWithChoices != null
+                    && smartRepliesAndActions.smartReplies != null
+                    && smartRepliesAndActions.smartReplies.length > 0) {
+                mSmartReplyController.smartRepliesAdded(entry,
+                        smartRepliesAndActions.smartReplies.length);
             }
         }
     }
 
-    private SmartReplyView applySmartReplyView(
-            View view, RemoteInput remoteInput, PendingIntent pendingIntent,
-            NotificationData.Entry entry, CharSequence[] choices) {
+    private SmartReplyView applySmartReplyView(View view,
+            SmartRepliesAndActions smartRepliesAndActions, NotificationData.Entry entry) {
         View smartReplyContainerCandidate = view.findViewById(
                 com.android.internal.R.id.smart_reply_container);
         if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
             return null;
         }
         LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
-        if (remoteInput == null || pendingIntent == null) {
+        // If there are no smart replies and no smart actions - early out.
+        if (!smartRepliesAndActions.smartRepliesExist()
+                && smartRepliesAndActions.smartActions.isEmpty()) {
             smartReplyContainer.setVisibility(View.GONE);
             return null;
         }
@@ -1468,9 +1524,11 @@
             }
         }
         if (smartReplyView != null) {
-            smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
-                    mSmartReplyController, entry, smartReplyContainer, choices
-            );
+            smartReplyView.resetSmartSuggestions(smartReplyContainer);
+            smartReplyView.addRepliesFromRemoteInput(smartRepliesAndActions.remoteInputWithChoices,
+                    smartRepliesAndActions.pendingIntentForSmartReplies, mSmartReplyController,
+                    entry, smartRepliesAndActions.smartReplies);
+            smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
             smartReplyContainer.setVisibility(View.VISIBLE);
         }
         return smartReplyView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index c16b28f..b6ff6fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -367,6 +367,9 @@
     public void onSnapOpen() {
         mMenuSnapped = true;
         mMenuSnappedOnLeft = isMenuOnLeft();
+        if (mAlpha == 0f && mParent != null) {
+            fadeInMenu(mParent.getWidth());
+        }
         if (mMenuListener != null) {
             mMenuListener.onMenuShown(getParent());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 2f58ca1..5db43ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -904,6 +904,7 @@
         boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
         int navBarPos = 0;
         try {
+            // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
             navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to get nav bar position.", e);
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 45e924f..bdddf5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2251,7 +2251,8 @@
     private void notifyUiVisibilityChanged(int vis) {
         try {
             if (mLastDispatchedSystemUiVisibility != vis) {
-                mWindowManagerService.statusBarVisibilityChanged(vis);
+                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
+                mWindowManagerService.statusBarVisibilityChanged(Display.DEFAULT_DISPLAY, vis);
                 mLastDispatchedSystemUiVisibility = vis;
             }
         } catch (RemoteException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 42f1378..0186683 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -1,6 +1,7 @@
 package com.android.systemui.statusbar.policy;
 
 import android.annotation.ColorInt;
+import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.content.Context;
@@ -19,6 +20,7 @@
 import android.text.method.TransformationMethod;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Size;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -30,6 +32,7 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.NotificationData;
@@ -38,14 +41,15 @@
 
 import java.text.BreakIterator;
 import java.util.Comparator;
+import java.util.List;
 import java.util.PriorityQueue;
 
-/** View which displays smart reply buttons in notifications. */
+/** View which displays smart reply and smart actions buttons in notifications. */
 public class SmartReplyView extends ViewGroup {
 
     private static final String TAG = "SmartReplyView";
 
-    private static final int MEASURE_SPEC_ANY_WIDTH =
+    private static final int MEASURE_SPEC_ANY_LENGTH =
             MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
 
     private static final Comparator<View> DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR =
@@ -98,6 +102,8 @@
     private final int mStrokeWidth;
     private final double mMinStrokeContrast;
 
+    private ActivityStarter mActivityStarter;
+
     public SmartReplyView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mConstants = Dependency.get(SmartReplyConstants.class);
@@ -168,13 +174,24 @@
                 Math.max(getChildCount(), 1), DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR);
     }
 
-    public void setRepliesFromRemoteInput(
-            RemoteInput remoteInput, PendingIntent pendingIntent,
-            SmartReplyController smartReplyController, NotificationData.Entry entry,
-            View smartReplyContainer, CharSequence[] choices) {
-        mSmartReplyContainer = smartReplyContainer;
+    /**
+     * Reset the smart suggestions view to allow adding new replies and actions.
+     */
+    public void resetSmartSuggestions(View newSmartReplyContainer) {
+        mSmartReplyContainer = newSmartReplyContainer;
         removeAllViews();
         mCurrentBackgroundColor = mDefaultBackgroundColor;
+    }
+
+    /**
+     * Add smart replies to this view, using the provided {@link RemoteInput} and
+     * {@link PendingIntent} to respond when the user taps a smart reply. Only the replies that fit
+     * into the notification are shown.
+     */
+    public void addRepliesFromRemoteInput(
+            RemoteInput remoteInput, PendingIntent pendingIntent,
+            SmartReplyController smartReplyController, NotificationData.Entry entry,
+            CharSequence[] choices) {
         if (remoteInput != null && pendingIntent != null) {
             if (choices != null) {
                 for (int i = 0; i < choices.length; ++i) {
@@ -188,6 +205,22 @@
         reallocateCandidateButtonQueueForSqueezing();
     }
 
+    /**
+     * Add smart actions to be shown next to smart replies. Only the actions that fit into the
+     * notification are shown.
+     */
+    public void addSmartActions(List<Notification.Action> smartActions) {
+        int numSmartActions = smartActions.size();
+        for (int n = 0; n < numSmartActions; n++) {
+            Notification.Action action = smartActions.get(n);
+            if (action.actionIntent != null) {
+                Button actionButton = inflateActionButton(getContext(), this, action);
+                addView(actionButton);
+            }
+        }
+        reallocateCandidateButtonQueueForSqueezing();
+    }
+
     public static SmartReplyView inflate(Context context, ViewGroup root) {
         return (SmartReplyView)
                 LayoutInflater.from(context).inflate(R.layout.smart_reply_view, root, false);
@@ -234,6 +267,48 @@
         return b;
     }
 
+    @VisibleForTesting
+    Button inflateActionButton(Context context, ViewGroup root, Notification.Action action) {
+        Button button = (Button) LayoutInflater.from(context).inflate(
+                R.layout.smart_action_button, root, false);
+        button.setText(action.title);
+
+        Drawable iconDrawable = action.getIcon().loadDrawable(context);
+        // Add the action icon to the Smart Action button.
+        Size newIconSize = calculateIconSizeFromSingleLineButton(context, root,
+                new Size(iconDrawable.getIntrinsicWidth(), iconDrawable.getIntrinsicHeight()));
+        iconDrawable.setBounds(0, 0, newIconSize.getWidth(), newIconSize.getHeight());
+        button.setCompoundDrawables(iconDrawable, null, null, null);
+
+        button.setOnClickListener(view ->
+                getActivityStarter().startPendingIntentDismissingKeyguard(action.actionIntent));
+
+        // TODO(b/119010281): handle accessibility
+
+        return button;
+    }
+
+    private static Size calculateIconSizeFromSingleLineButton(Context context, ViewGroup root,
+            Size originalIconSize) {
+        Button button = (Button) LayoutInflater.from(context).inflate(
+                R.layout.smart_action_button, root, false);
+        // Add simple text here to ensure the button displays one line of text.
+        button.setText("a");
+        return calculateIconSizeFromButtonHeight(button, originalIconSize);
+    }
+
+    // Given a button with text on a single line - we want to add an icon to that button. This
+    // method calculates the icon height to use to avoid making the button grow in height.
+    private static Size calculateIconSizeFromButtonHeight(Button button, Size originalIconSize) {
+        // A completely permissive measure spec should make the button text single-line.
+        button.measure(MEASURE_SPEC_ANY_LENGTH, MEASURE_SPEC_ANY_LENGTH);
+        int buttonHeight = button.getMeasuredHeight();
+        int newIconHeight = buttonHeight / 2;
+        int newIconWidth = (int) (originalIconSize.getWidth()
+                * ((double) newIconHeight) / originalIconSize.getHeight());
+        return new Size(newIconWidth, newIconHeight);
+    }
+
     @Override
     public LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new LayoutParams(mContext, attrs);
@@ -277,7 +352,7 @@
 
             child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
                     buttonPaddingHorizontal, child.getPaddingBottom());
-            child.measure(MEASURE_SPEC_ANY_WIDTH, heightMeasureSpec);
+            child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
 
             final int lineCount = ((Button) child).getLineCount();
             if (lineCount < 1 || lineCount > 2) {
@@ -437,6 +512,18 @@
         return (int) Math.ceil(optimalTextWidth);
     }
 
+    /**
+     * Returns the combined width of the left drawable (the action icon) and the padding between the
+     * drawable and the button text.
+     */
+    private int getLeftCompoundDrawableWidthWithPadding(Button button) {
+        Drawable[] drawables = button.getCompoundDrawables();
+        Drawable leftDrawable = drawables[0];
+        if (leftDrawable == null) return 0;
+
+        return leftDrawable.getBounds().width() + button.getCompoundDrawablePadding();
+    }
+
     private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) {
         int oldWidth = button.getMeasuredWidth();
         if (button.getPaddingLeft() != mDoubleLineButtonPaddingHorizontal) {
@@ -449,7 +536,8 @@
         button.setPadding(mDoubleLineButtonPaddingHorizontal, button.getPaddingTop(),
                 mDoubleLineButtonPaddingHorizontal, button.getPaddingBottom());
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                2 * mDoubleLineButtonPaddingHorizontal + textWidth, MeasureSpec.AT_MOST);
+                2 * mDoubleLineButtonPaddingHorizontal + textWidth
+                      + getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
         button.measure(widthMeasureSpec, heightMeasureSpec);
 
         final int newWidth = button.getMeasuredWidth();
@@ -607,6 +695,13 @@
         button.setTextColor(textColor);
     }
 
+    private ActivityStarter getActivityStarter() {
+        if (mActivityStarter == null) {
+            mActivityStarter = Dependency.get(ActivityStarter.class);
+        }
+        return mActivityStarter;
+    }
+
     @VisibleForTesting
     static class LayoutParams extends ViewGroup.LayoutParams {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index b44630a..221cbe9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -32,12 +32,13 @@
 import android.content.Intent;
 import android.os.BatteryManager;
 import android.os.HardwarePropertiesManager;
+import android.os.IThermalService;
 import android.os.PowerManager;
 import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.testing.TestableResources;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.R;
@@ -45,15 +46,16 @@
 import com.android.systemui.power.PowerUI.WarningsUI;
 import com.android.systemui.statusbar.phone.StatusBar;
 
-import java.time.Duration;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 @SmallTest
@@ -76,6 +78,7 @@
     private PowerUI mPowerUI;
     private EnhancedEstimates mEnhancedEstimates;
     @Mock private PowerManager mPowerManager;
+    @Mock private IThermalService mThermalServiceMock;
 
     @Before
     public void setup() {
@@ -541,5 +544,6 @@
         mPowerUI = new PowerUI();
         mPowerUI.mContext = mContext;
         mPowerUI.mComponents = mContext.getComponents();
+        mPowerUI.mThermalService = mThermalServiceMock;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index f59bfae..b3b45eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -39,6 +39,7 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.PendingIntent;
+import android.app.Person;
 import android.content.Intent;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
@@ -421,6 +422,33 @@
         assertEquals(snoozeCriterions, entry.snoozeCriteria);
     }
 
+    @Test
+    public void notificationDataEntry_testIsLastMessageFromReply() {
+        Person.Builder person = new Person.Builder()
+                .setName("name")
+                .setKey("abc")
+                .setUri("uri")
+                .setBot(true);
+
+        // EXTRA_MESSAGING_PERSON is the same Person as the sender in last message in EXTRA_MESSAGES
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person.build());
+        Bundle[] messagesBundle = new Bundle[]{ new Notification.MessagingStyle.Message(
+                "text", 0, person.build()).toBundle() };
+        bundle.putParcelableArray(Notification.EXTRA_MESSAGES, messagesBundle);
+
+        Notification notification = new Notification.Builder(mContext, "test")
+                .addExtras(bundle)
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+                notification, mContext.getUser(), "", 0);
+
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        entry.setHasSentReply();
+
+        assertTrue(entry.isLastMessageFromReply());
+    }
+
     private void initStatusBarNotification(boolean allowDuringSetup) {
         Bundle bundle = new Bundle();
         bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index c189c95..a6725b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -16,6 +16,11 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.Mockito.doNothing;
@@ -28,29 +33,62 @@
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.service.notification.StatusBarNotification;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.view.NotificationHeaderView;
 import android.view.View;
 
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class NotificationContentViewTest extends SysuiTestCase {
 
+    private static final String TEST_ACTION = "com.android.SMART_REPLY_VIEW_ACTION";
+
     NotificationContentView mView;
 
+    @Mock
+    SmartReplyConstants mSmartReplyConstants;
+    @Mock
+    StatusBarNotification mStatusBarNotification;
+    @Mock
+    Notification mNotification;
+    NotificationData.Entry mEntry;
+    @Mock
+    RemoteInput mRemoteInput;
+    @Mock
+    RemoteInput mFreeFormRemoteInput;
+
+    private Icon mActionIcon;
+
+
     @Before
     @UiThreadTest
     public void setup() {
+        MockitoAnnotations.initMocks(this);
+
         mView = new NotificationContentView(mContext, null);
         ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null);
         ExpandableNotificationRow mockRow = spy(row);
@@ -67,6 +105,12 @@
 
         mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
         mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+        // Smart replies
+        when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
+        mEntry = new NotificationData.Entry(mStatusBarNotification);
+        when(mSmartReplyConstants.isEnabled()).thenReturn(true);
+        mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
     }
 
     private View createViewWithHeight(int height) {
@@ -82,7 +126,7 @@
         mView.setDark(true, false, 0);
         mView.setDark(false, true, 0);
         mView.setHeadsUpAnimatingAway(true);
-        Assert.assertFalse(mView.isAnimatingVisibleType());
+        assertFalse(mView.isAnimatingVisibleType());
     }
 
     @Test
@@ -115,4 +159,161 @@
         verify(mockAmbient, never()).showAppOpsIcons(ops);
         verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
     }
+
+    private void setupAppGeneratedReplies(CharSequence[] smartReplyTitles) {
+        Notification.Action freeFormAction =
+                new Notification.Action.Builder(null, "Freeform Test Action", null).build();
+        setupAppGeneratedReplies(smartReplyTitles, freeFormAction);
+    }
+
+    private void setupAppGeneratedReplies(
+            CharSequence[] smartReplyTitles,
+            Notification.Action freeFormRemoteInputAction) {
+        Notification.Action action =
+                new Notification.Action.Builder(null, "Test Action", null).build();
+        when(mRemoteInput.getChoices()).thenReturn(smartReplyTitles);
+        Pair<RemoteInput, Notification.Action> remoteInputActionPair =
+                Pair.create(mRemoteInput, action);
+        when(mNotification.findRemoteInputActionPair(false)).thenReturn(remoteInputActionPair);
+
+        Pair<RemoteInput, Notification.Action> freeFormRemoteInputActionPair =
+                Pair.create(mFreeFormRemoteInput, freeFormRemoteInputAction);
+        when(mNotification.findRemoteInputActionPair(true)).thenReturn(
+                freeFormRemoteInputActionPair);
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartReplies() {
+        setupAppGeneratedReplies(new String[] {"Reply1", "Reply2"});
+        when(mSmartReplyConstants.isEnabled()).thenReturn(false);
+
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertFalse(repliesAndActions.smartRepliesExist());
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_appGeneratedSmartReplies() {
+        CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
+        setupAppGeneratedReplies(smartReplies);
+        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() {
+        CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
+        setupAppGeneratedReplies(smartReplies);
+        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+        List<Notification.Action> smartActions =
+                createActions(new String[] {"Test Action 1", "Test Action 2"});
+        when(mNotification.getContextualActions()).thenReturn(smartActions);
+
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
+        assertThat(repliesAndActions.smartActions, equalTo(smartActions));
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_sysGeneratedSmartReplies() {
+        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+                .setAllowGeneratedReplies(true)
+                .build();
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // replies.
+        setupAppGeneratedReplies(null, freeFormAction);
+
+        mEntry.smartReplies =
+                new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(mEntry.smartReplies));
+        assertThat(repliesAndActions.smartActions, is(empty()));
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed() {
+        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+                .setAllowGeneratedReplies(false)
+                .build();
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // replies.
+        setupAppGeneratedReplies(null, freeFormAction);
+
+        mEntry.smartReplies =
+                new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(null));
+        assertThat(repliesAndActions.smartActions, is(empty()));
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_sysGeneratedSmartActions() {
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // actions.
+        setupAppGeneratedReplies(null);
+
+        mEntry.systemGeneratedSmartActions =
+                createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(null));
+        assertThat(repliesAndActions.smartActions, equalTo(mEntry.systemGeneratedSmartActions));
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_appGenPreferredOverSysGen() {
+        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+                .setAllowGeneratedReplies(true)
+                .build();
+        CharSequence[] appGenSmartReplies = new String[] {"Reply1", "Reply2"};
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // replies.
+        setupAppGeneratedReplies(appGenSmartReplies, freeFormAction);
+        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+        List<Notification.Action> appGenSmartActions =
+                createActions(new String[] {"Test Action 1", "Test Action 2"});
+        when(mNotification.getContextualActions()).thenReturn(appGenSmartActions);
+
+        mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        mEntry.systemGeneratedSmartActions =
+                createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(appGenSmartReplies));
+        assertThat(repliesAndActions.smartActions, equalTo(appGenSmartActions));
+    }
+
+    private Notification.Action.Builder createActionBuilder(String actionTitle) {
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                new Intent(TEST_ACTION), 0);
+        return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent);
+    }
+
+    private Notification.Action createAction(String actionTitle) {
+        return createActionBuilder(actionTitle).build();
+    }
+
+    private List<Notification.Action> createActions(String[] actionTitles) {
+        List<Notification.Action> actions = new ArrayList<>();
+        for (String title : actionTitles) {
+            actions.add(createAction(title));
+        }
+        return actions;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 4534ebe..9e659c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -22,7 +22,9 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -32,6 +34,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -41,14 +45,14 @@
 import android.widget.Button;
 import android.widget.LinearLayout;
 
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-
-import java.util.concurrent.atomic.AtomicReference;
+import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.After;
 import org.junit.Before;
@@ -57,6 +61,10 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
@@ -67,6 +75,10 @@
     private static final String[] TEST_CHOICES = new String[]{"Hello", "What's up?", "I'm here"};
     private static final String TEST_NOTIFICATION_KEY = "akey";
 
+    private static final String[] TEST_ACTION_TITLES = new String[]{
+            "First action", "Open something", "Action"
+    };
+
     private static final int WIDTH_SPEC = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
     private static final int HEIGHT_SPEC = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
 
@@ -74,6 +86,8 @@
     private SmartReplyView mView;
     private View mContainer;
 
+    private Icon mActionIcon;
+
     private int mSingleLinePaddingHorizontal;
     private int mDoubleLinePaddingHorizontal;
     private int mSpacing;
@@ -82,12 +96,16 @@
     private NotificationData.Entry mEntry;
     private Notification mNotification;
 
+    @Mock ActivityStarter mActivityStarter;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
         mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> action.onDismiss());
+        mDependency.injectMockDependency(ShadeController.class);
+        mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
 
         mContainer = new View(mContext, null);
         mView = SmartReplyView.inflate(mContext, null);
@@ -108,6 +126,8 @@
         when(sbn.getNotification()).thenReturn(mNotification);
         when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
         mEntry = new NotificationData.Entry(sbn);
+
+        mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
     }
 
     @After
@@ -117,7 +137,7 @@
 
     @Test
     public void testSendSmartReply_intentContainsResultsAndSource() throws InterruptedException {
-        setRepliesFromRemoteInput(TEST_CHOICES);
+        setSmartReplies(TEST_CHOICES);
 
         mView.getChildAt(2).performClick();
 
@@ -130,7 +150,7 @@
     @Test
     public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
         mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> {});
-        setRepliesFromRemoteInput(TEST_CHOICES);
+        setSmartReplies(TEST_CHOICES);
 
         mView.getChildAt(2).performClick();
 
@@ -141,7 +161,7 @@
     public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
         AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
         mDependency.get(KeyguardDismissUtil.class).setDismissHandler(actionRef::set);
-        setRepliesFromRemoteInput(TEST_CHOICES);
+        setSmartReplies(TEST_CHOICES);
 
         mView.getChildAt(2).performClick();
 
@@ -159,7 +179,7 @@
 
     @Test
     public void testSendSmartReply_controllerCalled() {
-        setRepliesFromRemoteInput(TEST_CHOICES);
+        setSmartReplies(TEST_CHOICES);
         mView.getChildAt(2).performClick();
         verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2]);
     }
@@ -167,7 +187,7 @@
     @Test
     public void testSendSmartReply_hidesContainer() {
         mContainer.setVisibility(View.VISIBLE);
-        setRepliesFromRemoteInput(TEST_CHOICES);
+        setSmartReplies(TEST_CHOICES);
         mView.getChildAt(0).performClick();
         assertEquals(View.GONE, mContainer.getVisibility());
     }
@@ -198,7 +218,7 @@
         ViewGroup expectedView = buildExpectedView(choices, 1);
         expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
         assertEqualMeasures(expectedView, mView);
@@ -217,7 +237,7 @@
         expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
                 10 + expectedView.getMeasuredHeight());
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
 
@@ -235,7 +255,7 @@
         ViewGroup expectedView = buildExpectedView(choices, 2);
         expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
         assertEqualMeasures(expectedView, mView);
@@ -254,7 +274,7 @@
         expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
                 10 + expectedView.getMeasuredHeight());
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
 
@@ -273,7 +293,7 @@
         ViewGroup expectedView = buildExpectedView(new CharSequence[]{"Hi", "Bye"}, 1);
         expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
         assertEqualMeasures(expectedView, mView);
@@ -293,7 +313,7 @@
         expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
                 10 + expectedView.getMeasuredHeight());
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
 
@@ -313,7 +333,7 @@
                 new CharSequence[]{"Short", "Short", "Looooooong \nreplyyyyy"}, 2);
         expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(
                 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
                 MeasureSpec.UNSPECIFIED);
@@ -335,7 +355,7 @@
         expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
                 10 + expectedView.getMeasuredHeight());
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(
                 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
                 MeasureSpec.UNSPECIFIED);
@@ -359,7 +379,7 @@
         expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
                 10 + expectedView.getMeasuredHeight());
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(
                 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
                 MeasureSpec.UNSPECIFIED);
@@ -371,15 +391,45 @@
         assertReplyButtonHidden(mView.getChildAt(2));
     }
 
-    private void setRepliesFromRemoteInput(CharSequence[] choices) {
+    private void setSmartReplies(CharSequence[] choices) {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
                 new Intent(TEST_ACTION), 0);
         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
-        mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, mContainer, choices);
+        mView.resetSmartSuggestions(mContainer);
+        mView.addRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, choices);
+    }
+
+    private Notification.Action createAction(String actionTitle) {
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                new Intent(TEST_ACTION), 0);
+        return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent).build();
+    }
+
+    private List<Notification.Action> createActions(String[] actionTitles) {
+        List<Notification.Action> actions = new ArrayList<>();
+        for (String title : actionTitles) {
+            actions.add(createAction(title));
+        }
+        return actions;
+    }
+
+    private void setSmartActions(String[] actionTitles) {
+        mView.resetSmartSuggestions(mContainer);
+        mView.addSmartActions(createActions(actionTitles));
+    }
+
+    private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
+        setSmartReplies(choices);
+        mView.addSmartActions(createActions(actionTitles));
+    }
+
+    private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
+        return buildExpectedView(choices, lineCount, new ArrayList<>());
     }
 
     /** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
-    private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
+    private ViewGroup buildExpectedView(
+            CharSequence[] choices, int lineCount, List<Notification.Action> actions) {
         LinearLayout layout = new LinearLayout(mContext);
         layout.setOrientation(LinearLayout.HORIZONTAL);
 
@@ -401,6 +451,7 @@
                 return null;
         }
 
+        // Add smart replies
         Button previous = null;
         for (int i = 0; i < choices.length; ++i) {
             Button current = mView.inflateReplyButton(mContext, mView, i, choices[i],
@@ -420,6 +471,24 @@
             previous = current;
         }
 
+        // Add smart actions
+        for (int i = 0; i < actions.size(); ++i) {
+            Button current = inflateActionButton(actions.get(i));
+            current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
+                    current.getPaddingBottom());
+            if (previous != null) {
+                ViewGroup.MarginLayoutParams lp =
+                        (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
+                if (isRtl) {
+                    lp.leftMargin = mSpacing;
+                } else {
+                    lp.rightMargin = mSpacing;
+                }
+            }
+            layout.addView(current);
+            previous = current;
+        }
+
         return layout;
     }
 
@@ -455,4 +524,255 @@
         assertEquals(expected.getPaddingRight(), actual.getPaddingRight());
         assertEquals(expected.getPaddingBottom(), actual.getPaddingBottom());
     }
+
+
+    // =============================================================================================
+    // ============================= Smart Action tests ============================================
+    // =============================================================================================
+
+    @Test
+    public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
+        setSmartActions(TEST_ACTION_TITLES);
+
+        mView.getChildAt(2).performClick();
+
+        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any());
+    }
+
+    @Test
+    public void testMeasure_shortSmartActions() {
+        String[] actions = new String[] {"Hi", "Hello", "Bye"};
+        // All choices should be displayed as SINGLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testLayout_shortSmartActions() {
+        String[] actions = new String[] {"Hi", "Hello", "Bye"};
+        // All choices should be displayed as SINGLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+                10 + expectedView.getMeasuredHeight());
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+        assertEqualLayouts(expectedView, mView);
+        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testMeasure_smartActionWithTwoLines() {
+        String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testLayout_smartActionWithTwoLines() {
+        String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+                10 + expectedView.getMeasuredHeight());
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+        assertEqualLayouts(expectedView, mView);
+        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testMeasure_smartActionWithThreeLines() {
+        String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
+
+        // The action with three lines should NOT be displayed. All other actions should be
+        // displayed as SINGLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
+                createActions(new String[]{"Hi", "Bye"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonHidden(mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testLayout_smartActionWithThreeLines() {
+        String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
+
+        // The action with three lines should NOT be displayed. All other actions should be
+        // displayed as SINGLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
+                createActions(new String[]{"Hi", "Bye"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+                10 + expectedView.getMeasuredHeight());
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+        assertEqualLayouts(expectedView, mView);
+        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+        // We don't care about mView.getChildAt(1)'s layout because it's hidden (see
+        // testMeasure_smartActionWithThreeLines).
+        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testMeasure_squeezeLongestSmartAction() {
+        String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
+                createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartActions(actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testLayout_squeezeLongestSmartAction() {
+        String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
+                createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+                10 + expectedView.getMeasuredHeight());
+
+        setSmartActions(actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+        mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+        assertEqualLayouts(expectedView, mView);
+        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testMeasure_dropLongestSmartAction() {
+        String[] actions = new String[] {"Short", "Short", "LooooooongUnbreakableReplyyyyy"};
+
+        // Short actions should be shown as single line views
+        ViewGroup expectedView = buildExpectedView(
+                new CharSequence[0], 1, createActions(new String[] {"Short", "Short"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+                10 + expectedView.getMeasuredHeight());
+
+        setSmartActions(actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+        mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+        assertEqualLayouts(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonHidden(mView.getChildAt(2));
+    }
+
+    private Button inflateActionButton(Notification.Action action) {
+        return mView.inflateActionButton(getContext(), mView, action);
+    }
+
+    @Test
+    public void testInflateActionButton_smartActionIconSingleLineSizeForTwoLineButton() {
+        // Ensure smart action icons are the same size regardless of the number of text rows in the
+        // button.
+        Button singleLineButton = inflateActionButton(createAction("One line"));
+        Button doubleLineButton = inflateActionButton(createAction("Two\nlines"));
+        Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable
+        Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable
+        assertEquals(singleLineDrawable.getBounds().width(),
+                     doubleLineDrawable.getBounds().width());
+        assertEquals(singleLineDrawable.getBounds().height(),
+                     doubleLineDrawable.getBounds().height());
+    }
+
+    @Test
+    public void testMeasure_shortChoicesAndActions() {
+        CharSequence[] choices = new String[] {"Hi", "Hello"};
+        String[] actions = new String[] {"Bye"};
+        // All choices should be displayed as SINGLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(choices, 1, createActions(actions));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testMeasure_choicesAndActionsSqueezeLongestAction() {
+        CharSequence[] choices = new String[] {"Short", "Short"};
+        String[] actions = new String[] {"Looooooong replyyyyy"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(choices, 2,
+                createActions(new String[] {"Looooooong \nreplyyyyy"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 7621ada..e4b4bc5 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1388,6 +1388,7 @@
                     app = mPackageManager.getApplicationInfo(pkg.packageName,
                             PackageManager.GET_SHARED_LIBRARY_FILES);
                     pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
+                    pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
                 }
             } catch (NameNotFoundException e) {
                 packages.remove(a);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c660cc6..a19e928 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1702,8 +1702,11 @@
                     s.app.whitelistManager = true;
                 }
                 // This could have made the service more important.
-                mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities()
-                        || s.app.treatLikeActivity, b.client);
+                mAm.updateLruProcessLocked(s.app,
+                        (callerApp.hasActivitiesOrRecentTasks() && s.app.hasClientActivities())
+                                || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
+                                        && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
+                        b.client);
                 mAm.updateOomAdjLocked(s.app, true);
             }
 
@@ -1787,6 +1790,32 @@
         }
     }
 
+    void updateServiceGroupLocked(IServiceConnection connection, int group, int importance) {
+        final IBinder binder = connection.asBinder();
+        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "updateServiceGroup: conn=" + binder);
+        final ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+        if (clist == null) {
+            throw new IllegalArgumentException("Could not find connection for "
+                    + connection.asBinder());
+        }
+        for (int i = clist.size() - 1; i >= 0; i--) {
+            final ConnectionRecord crec = clist.get(i);
+            final ServiceRecord srec = crec.binding.service;
+            if (srec != null && srec.app != null
+                    && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+                if (group > 0) {
+                    srec.app.connectionService = srec;
+                    srec.app.connectionGroup = group;
+                    srec.app.connectionImportance = importance;
+                } else {
+                    srec.app.connectionService = null;
+                    srec.app.connectionGroup = 0;
+                    srec.app.connectionImportance = 0;
+                }
+            }
+        }
+    }
+
     boolean unbindServiceLocked(IServiceConnection connection) {
         IBinder binder = connection.asBinder();
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "unbindService: conn=" + binder);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4e417ba..7e9e83c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9207,26 +9207,33 @@
                 }
                 dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
             }
+            if (dumpPackage == null) {
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                mOomAdjProfiler.dump(pw);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpBinderProxies(pw);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpLmkLocked(pw);
+            }
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpLruLocked(pw, dumpPackage);
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
             }
             dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            mOomAdjProfiler.dump(pw);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            dumpBinderProxies(pw);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            dumpLmkLocked(pw);
         }
     }
 
@@ -9421,6 +9428,10 @@
                 synchronized (this) {
                     dumpLmkLocked(pw);
                 }
+            } else if ("lru".equals(cmd)) {
+                synchronized (this) {
+                    dumpLruLocked(pw, null);
+                }
             } else if ("permissions".equals(cmd) || "perm".equals(cmd)) {
                 synchronized (this) {
                     dumpPermissionsLocked(fd, pw, args, opti, true, null);
@@ -9698,17 +9709,102 @@
                 }
                 pw.println();
             }
-            pw.println();
             return true;
         }
         return false;
     }
 
     void dumpBinderProxies(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER BINDER PROXY STATE (dumpsys activity binder-proxies)");
         dumpBinderProxyInterfaceCounts(pw,
-                "Top proxy interface names held by SYSTEM");
+                "  Top proxy interface names held by SYSTEM");
         dumpBinderProxiesCounts(pw,
-                "Counts of Binder Proxies held by SYSTEM");
+                "  Counts of Binder Proxies held by SYSTEM");
+    }
+
+    void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc) {
+        pw.print("    #");
+        pw.print(index);
+        pw.print(": ");
+        pw.print(ProcessList.makeOomAdjString(proc.setAdj));
+        pw.print(" ");
+        pw.print(ProcessList.makeProcStateString(proc.getCurProcState()));
+        pw.print(" ");
+        pw.print(proc.toShortString());
+        pw.print(" ");
+        if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities()
+                || proc.treatLikeActivity) {
+            pw.print(" activity=");
+            boolean printed = false;
+            if (proc.hasActivities()) {
+                pw.print("activities");
+                printed = true;
+            }
+            if (proc.hasRecentTasks()) {
+                if (printed) {
+                    pw.print("|");
+                }
+                pw.print("recents");
+                printed = true;
+            }
+            if (proc.hasClientActivities()) {
+                if (printed) {
+                    pw.print("|");
+                }
+                pw.print("client");
+                printed = true;
+            }
+            if (proc.treatLikeActivity) {
+                if (printed) {
+                    pw.print("|");
+                }
+                pw.print("treated");
+            }
+        }
+        pw.println();
+    }
+
+    // TODO: Move to ProcessList?
+    void dumpLruLocked(PrintWriter pw, String dumpPackage) {
+        pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity lru)");
+        final int N = mProcessList.mLruProcesses.size();
+        int i;
+        boolean first = true;
+        for (i = N - 1; i >= mProcessList.mLruProcessActivityStart; i--) {
+            final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+            if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+                continue;
+            }
+            if (first) {
+                pw.println("  Activities:");
+                first = false;
+            }
+            dumpLruEntryLocked(pw, i, r);
+        }
+        first = true;
+        for (; i >= mProcessList.mLruProcessServiceStart; i--) {
+            final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+            if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+                continue;
+            }
+            if (first) {
+                pw.println("  Services:");
+                first = false;
+            }
+            dumpLruEntryLocked(pw, i, r);
+        }
+        first = true;
+        for (; i >= 0; i--) {
+            final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+            if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+                continue;
+            }
+            if (first) {
+                pw.println("  Other:");
+                first = false;
+            }
+            dumpLruEntryLocked(pw, i, r);
+        }
     }
 
     // TODO: Move to ProcessList?
@@ -13214,6 +13310,12 @@
         }
     }
 
+    public void updateServiceGroup(IServiceConnection connection, int group, int importance) {
+        synchronized (this) {
+            mServices.updateServiceGroupLocked(connection, group, importance);
+        }
+    }
+
     public boolean unbindService(IServiceConnection connection) {
         synchronized (this) {
             return mServices.unbindServiceLocked(connection);
@@ -17398,8 +17500,11 @@
         int stepCached = 0;
         int stepEmpty = 0;
         int numCached = 0;
+        int numCachedExtraGroup = 0;
         int numEmpty = 0;
         int numTrimming = 0;
+        int lastCachedGroup = 0;
+        int lastCachedGroupUid = 0;
 
         mNumNonCachedProcs = 0;
         mNumCachedHiddenProcs = 0;
@@ -17523,7 +17628,21 @@
                     case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                         mNumCachedHiddenProcs++;
                         numCached++;
-                        if (numCached > cachedProcessLimit) {
+                        if (app.connectionGroup != 0) {
+                            if (lastCachedGroupUid == app.uid
+                                    && lastCachedGroup == app.connectionGroup) {
+                                // If this process is the next in the same group, we don't
+                                // want it to count against our limit of the number of cached
+                                // processes, so bump up the group count to account for it.
+                                numCachedExtraGroup++;
+                            } else {
+                                lastCachedGroupUid = app.uid;
+                                lastCachedGroup = app.connectionGroup;
+                            }
+                        } else {
+                            lastCachedGroupUid = lastCachedGroup = 0;
+                        }
+                        if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
                             app.kill("cached #" + numCached, true);
                         }
                         break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 8f8d5ab..67a4d14 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2853,6 +2853,9 @@
             pw.println("    provider [COMP_SPEC]: provider client-side state");
             pw.println("    s[ervices] [COMP_SPEC ...]: service state");
             pw.println("    as[sociations]: tracked app associations");
+            pw.println("    lmk: stats on low memory killer");
+            pw.println("    lru: raw LRU process list");
+            pw.println("    binder-proxies: stats on binder objects and IPCs");
             pw.println("    settings: currently applied config settings");
             pw.println("    service [COMP_SPEC]: service client-side state");
             pw.println("    package [PACKAGE_NAME]: all state related to given package");
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index bfa3f66..aa76b3d 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -48,7 +48,7 @@
     boolean serviceDead;            // Well is it?
 
     // Please keep the following two enum list synced.
-    private static int[] BIND_ORIG_ENUMS = new int[] {
+    private static final int[] BIND_ORIG_ENUMS = new int[] {
             Context.BIND_AUTO_CREATE,
             Context.BIND_DEBUG_UNBIND,
             Context.BIND_NOT_FOREGROUND,
@@ -65,7 +65,7 @@
             Context.BIND_SHOWING_UI,
             Context.BIND_NOT_VISIBLE,
     };
-    private static int[] BIND_PROTO_ENUMS = new int[] {
+    private static final int[] BIND_PROTO_ENUMS = new int[] {
             ConnectionRecordProto.AUTO_CREATE,
             ConnectionRecordProto.DEBUG_UNBIND,
             ConnectionRecordProto.NOT_FG,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 84b364b..4b19398 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -50,6 +50,7 @@
 import android.app.AppProtoEnums;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
@@ -2204,7 +2205,7 @@
 
     @GuardedBy("mService")
     int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
-            String what, Object obj, ProcessRecord srcApp) {
+            int lruSeq, String what, Object obj, ProcessRecord srcApp) {
         app.lastActivityTime = now;
 
         if (app.hasActivitiesOrRecentTasks()) {
@@ -2225,7 +2226,7 @@
             return index;
         }
 
-        if (lrui >= mLruProcessActivityStart) {
+        if (lrui >= mLruProcessActivityStart && index < mLruProcessActivityStart) {
             // Don't want to touch dependent processes that are hosting activities.
             return index;
         }
@@ -2237,6 +2238,7 @@
         if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
                 + " in LRU list: " + app);
         mLruProcesses.add(index, app);
+        app.lruSeq = lruSeq;
         return index;
     }
 
@@ -2345,9 +2347,11 @@
         */
 
         int nextIndex;
+        int nextActivityIndex = -1;
         if (hasActivity) {
             final int N = mLruProcesses.size();
-            if ((!app.hasActivities() || app.hasRecentTasks())
+            nextIndex = mLruProcessServiceStart;
+            if (!app.hasActivitiesOrRecentTasks() && !app.treatLikeActivity
                     && mLruProcessActivityStart < (N - 1)) {
                 // Process doesn't have activities, but has clients with
                 // activities...  move it up, but one below the top (the top
@@ -2355,36 +2359,92 @@
                 if (DEBUG_LRU) Slog.d(TAG_LRU,
                         "Adding to second-top of LRU activity list: " + app);
                 mLruProcesses.add(N - 1, app);
-                // To keep it from spamming the LRU list (by making a bunch of clients),
-                // we will push down any other entries owned by the app.
+                // If this process is part of a group, need to pull up any other processes
+                // in that group to be with it.
                 final int uid = app.info.uid;
-                for (int i = N - 2; i > mLruProcessActivityStart; i--) {
-                    ProcessRecord subProc = mLruProcesses.get(i);
-                    if (subProc.info.uid == uid) {
-                        // We want to push this one down the list.  If the process after
-                        // it is for the same uid, however, don't do so, because we don't
-                        // want them internally to be re-ordered.
-                        if (mLruProcesses.get(i - 1).info.uid != uid) {
-                            if (DEBUG_LRU) Slog.d(TAG_LRU,
-                                    "Pushing uid " + uid + " swapping at " + i + ": "
-                                            + mLruProcesses.get(i) + " : "
-                                            + mLruProcesses.get(i - 1));
-                            ProcessRecord tmp = mLruProcesses.get(i);
-                            mLruProcesses.set(i, mLruProcesses.get(i - 1));
-                            mLruProcesses.set(i - 1, tmp);
-                            i--;
+                int endIndex = N - 2;
+                nextActivityIndex = N - 2;
+                if (app.connectionGroup > 0) {
+                    int endImportance = app.connectionImportance;
+                    for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
+                        final ProcessRecord subProc = mLruProcesses.get(i);
+                        if (subProc.info.uid == uid
+                                && subProc.connectionGroup == subProc.connectionGroup) {
+                            if (i == endIndex && subProc.connectionImportance >= endImportance) {
+                                // This process is already in the group, and its importance
+                                // is not as strong as the process before it, so it keep it
+                                // correctly positioned in the group.
+                                endIndex--;
+                                endImportance = subProc.connectionImportance;
+                            } else {
+                                // We want to pull this up to be with the rest of the group,
+                                // and order within the group by importance.
+                                boolean moved = false;
+                                for (int pos = N - 1; pos > endIndex; pos--) {
+                                    final ProcessRecord posProc = mLruProcesses.get(pos);
+                                    if (subProc.connectionImportance
+                                            <= posProc.connectionImportance) {
+                                        mLruProcesses.remove(i);
+                                        mLruProcesses.add(pos, subProc);
+                                        moved = true;
+                                        endIndex--;
+                                        break;
+                                    }
+                                }
+                                if (!moved) {
+                                    // Goes to the end of the group.
+                                    mLruProcesses.remove(i);
+                                    mLruProcesses.add(endIndex - 1, subProc);
+                                    endIndex--;
+                                    endImportance = subProc.connectionImportance;
+                                }
+                            }
                         }
-                    } else {
-                        // A gap, we can stop here.
-                        break;
+                    }
+
+                }
+                // To keep it from spamming the LRU list (by making a bunch of clients),
+                // we will distribute other entries owned by it to be in-between other apps.
+                for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
+                    final ProcessRecord subProc = mLruProcesses.get(i);
+                    if (subProc.info.uid != uid) {
+                        // This is a different app...  if we have gone through some of the
+                        // target app, pull this up to be before them.
+                        if (i < endIndex) {
+                            mLruProcesses.remove(i);
+                            mLruProcesses.add(endIndex, subProc);
+                        }
+                        // Find the end of the next group of processes for target app.  This
+                        // is after any entries of different apps (so we don't change the existing
+                        // relative order of apps) and then after the next last group of processes
+                        // of the target app.
+                        for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
+                            final ProcessRecord endProc = mLruProcesses.get(endIndex);
+                            if (endProc.info.uid == uid) {
+                                break;
+                            }
+                        }
+                        if (endIndex >= mLruProcessActivityStart) {
+                            final ProcessRecord endProc = mLruProcesses.get(endIndex);
+                            for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
+                                final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
+                                if (nextEndProc.info.uid != uid
+                                        || nextEndProc.connectionGroup != endProc.connectionGroup) {
+                                    break;
+                                }
+                            }
+                        }
+                        if (i > endIndex) {
+                            i = endIndex;
+                        }
                     }
                 }
             } else {
                 // Process has activities, put it at the very tipsy-top.
                 if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
                 mLruProcesses.add(app);
+                nextActivityIndex = mLruProcesses.size() - 1;
             }
-            nextIndex = mLruProcessServiceStart;
         } else if (hasService) {
             // Process has services, put it at the top of the service list.
             if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
@@ -2416,6 +2476,8 @@
             mLruProcessServiceStart++;
         }
 
+        app.lruSeq = mLruSeq;
+
         // If the app is currently using a content provider or service,
         // bump those processes as well.
         for (int j = app.connections.size() - 1; j >= 0; j--) {
@@ -2423,17 +2485,27 @@
             if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                     && cr.binding.service.app != null
                     && cr.binding.service.app.lruSeq != mLruSeq
+                    && (cr.flags & Context.BIND_REDUCTION_FLAGS) == 0
                     && !cr.binding.service.app.isPersistent()) {
-                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
-                        now,
-                        nextIndex,
-                        "service connection", cr, app);
+                if (cr.binding.service.app.hasClientActivities()) {
+                    if (nextActivityIndex >= 0) {
+                        nextActivityIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+                                now,
+                                nextActivityIndex, mLruSeq,
+                                "service connection", cr, app);
+                    }
+                } else {
+                    nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+                            now,
+                            nextIndex, mLruSeq,
+                            "service connection", cr, app);
+                }
             }
         }
         for (int j = app.conProviders.size() - 1; j >= 0; j--) {
             ContentProviderRecord cpr = app.conProviders.get(j).provider;
             if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
-                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
+                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, mLruSeq,
                         "provider reference", cpr, app);
             }
         }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index faf8561..013de93 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -155,6 +155,9 @@
     int pssStatType;            // The type of stat collection that we are currently requesting
     int savedPriority;          // Previous priority value if we're switching to non-SCHED_OTHER
     int renderThreadTid;        // TID for RenderThread
+    ServiceRecord connectionService; // Service that applied current connectionGroup/Importance
+    int connectionGroup;        // Last group set by a connection
+    int connectionImportance;   // Last importance set by a connection
     boolean serviceb;           // Process currently is on the service B list
     boolean serviceHighRam;     // We are forcing to service B list due to its RAM use
     boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
@@ -396,6 +399,11 @@
                     pw.print(" hasAboveClient="); pw.print(hasAboveClient);
                     pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
         }
+        if (connectionService != null || connectionGroup != 0) {
+            pw.print(prefix); pw.print("connectionGroup="); pw.print(connectionGroup);
+            pw.print(" Importance="); pw.print(connectionImportance);
+            pw.print(" Service="); pw.println(connectionService);
+        }
         if (hasTopUi() || hasOverlayUi() || runningRemoteAnimation) {
             pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
                     pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b442072..1dfb86a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -94,6 +94,7 @@
 import android.service.vr.IVrStateCallbacks;
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.EventLog;
@@ -179,7 +180,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.WeakHashMap;
@@ -321,7 +321,7 @@
     // All known input methods.  mMethodMap also serves as the global
     // lock for this class.
     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
-    final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<>();
+    final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
     private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
             new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
     private final InputMethodSubtypeSwitchingController mSwitchingController;
@@ -457,7 +457,7 @@
         }
     }
 
-    final HashMap<IBinder, ClientState> mClients = new HashMap<>();
+    final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
 
     /**
      * Set once the system is ready to run third party code.
@@ -553,8 +553,8 @@
     private InputMethodSubtype mCurrentSubtype;
 
     // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
-    private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
-            mShortcutInputMethodsAndSubtypes = new HashMap<>();
+    private final ArrayMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
+            mShortcutInputMethodsAndSubtypes = new ArrayMap<>();
 
     // Was the keyguard locked when this client became current?
     private boolean mCurClientInKeyguard;
@@ -1781,7 +1781,9 @@
         final int callerPid = Binder.getCallingPid();
         synchronized (mMethodMap) {
             // TODO: Optimize this linear search.
-            for (ClientState state : mClients.values()) {
+            final int numClients = mClients.size();
+            for (int i = 0; i < numClients; ++i) {
+                final ClientState state = mClients.valueAt(i);
                 if (state.uid == callerUid && state.pid == callerPid
                         && state.selfReportedDisplayId == selfReportedDisplayId) {
                     throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
@@ -2192,8 +2194,9 @@
 
     void clearCurMethodLocked() {
         if (mCurMethod != null) {
-            for (ClientState cs : mClients.values()) {
-                clearClientSessionLocked(cs);
+            final int numClients = mClients.size();
+            for (int i = 0; i < numClients; ++i) {
+                clearClientSessionLocked(mClients.valueAt(i));
             }
 
             finishSessionLocked(mEnabledSession);
@@ -3670,7 +3673,7 @@
                         | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS),
                 mSettings.getCurrentUserId());
 
-        final HashMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+        final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
                 mFileManager.getAllAdditionalInputMethodSubtypes();
         for (int i = 0; i < services.size(); ++i) {
             ResolveInfo ri = services.get(i);
@@ -4306,10 +4309,10 @@
         private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
         private static final String ATTR_IS_ASCII_CAPABLE = "isAsciiCapable";
         private final AtomicFile mAdditionalInputMethodSubtypeFile;
-        private final HashMap<String, InputMethodInfo> mMethodMap;
-        private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
-                new HashMap<>();
-        public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap, int userId) {
+        private final ArrayMap<String, InputMethodInfo> mMethodMap;
+        private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
+                new ArrayMap<>();
+        InputMethodFileManager(ArrayMap<String, InputMethodInfo> methodMap, int userId) {
             if (methodMap == null) {
                 throw new NullPointerException("methodMap is null");
             }
@@ -4361,15 +4364,15 @@
             }
         }
 
-        public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
+        public ArrayMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
             synchronized (mMethodMap) {
                 return mAdditionalSubtypesMap;
             }
         }
 
         private static void writeAdditionalInputMethodSubtypes(
-                HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
-                HashMap<String, InputMethodInfo> methodMap) {
+                ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
+                ArrayMap<String, InputMethodInfo> methodMap) {
             // Safety net for the case that this function is called before methodMap is set.
             final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
             FileOutputStream fos = null;
@@ -4423,7 +4426,7 @@
         }
 
         private static void readAdditionalInputMethodSubtypes(
-                HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
+                ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
             if (allSubtypes == null || subtypesFile == null) return;
             allSubtypes.clear();
             try (final FileInputStream fis = subtypesFile.openRead()) {
@@ -4621,7 +4624,9 @@
                 info.dump(p, "    ");
             }
             p.println("  Clients:");
-            for (ClientState ci : mClients.values()) {
+            final int numClients = mClients.size();
+            for (int i = 0; i < numClients; ++i) {
+                final ClientState ci = mClients.valueAt(i);
                 p.println("  Client " + ci + ":");
                 p.println("    client=" + ci.client);
                 p.println("    inputContext=" + ci.inputContext);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index db6d826..8e3f351 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -31,6 +31,7 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Pair;
 import android.util.Printer;
 import android.util.Slog;
@@ -45,7 +46,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
@@ -473,7 +473,7 @@
         final int numSubtypes = subtypes.size();
 
         // Handle overridesImplicitlyEnabledSubtype mechanism.
-        final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new HashMap<>();
+        final ArrayMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new ArrayMap<>();
         for (int i = 0; i < numSubtypes; ++i) {
             // scan overriding implicitly enabled subtypes.
             final InputMethodSubtype subtype = subtypes.get(i);
@@ -488,8 +488,8 @@
             return new ArrayList<>(applicableModeAndSubtypesMap.values());
         }
 
-        final HashMap<String, ArrayList<InputMethodSubtype>> nonKeyboardSubtypesMap =
-                new HashMap<>();
+        final ArrayMap<String, ArrayList<InputMethodSubtype>> nonKeyboardSubtypesMap =
+                new ArrayMap<>();
         final ArrayList<InputMethodSubtype> keyboardSubtypes = new ArrayList<>();
 
         for (int i = 0; i < numSubtypes; ++i) {
@@ -761,12 +761,12 @@
 
         private final Resources mRes;
         private final ContentResolver mResolver;
-        private final HashMap<String, InputMethodInfo> mMethodMap;
+        private final ArrayMap<String, InputMethodInfo> mMethodMap;
 
         /**
          * On-memory data store to emulate when {@link #mCopyOnWrite} is {@code true}.
          */
-        private final HashMap<String, String> mCopyOnWriteDataStore = new HashMap<>();
+        private final ArrayMap<String, String> mCopyOnWriteDataStore = new ArrayMap<>();
 
         private boolean mCopyOnWrite = false;
         @NonNull
@@ -812,7 +812,7 @@
 
         public InputMethodSettings(
                 Resources res, ContentResolver resolver,
-                HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
+                ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
                 @UserIdInt int userId, boolean copyOnWrite) {
             mRes = res;
             mResolver = resolver;
diff --git a/services/core/java/com/android/server/inputmethod/LocaleUtils.java b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
index 4958ece..7a6853a 100644
--- a/services/core/java/com/android/server/inputmethod/LocaleUtils.java
+++ b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
@@ -22,10 +22,10 @@
 import android.icu.util.ULocale;
 import android.os.LocaleList;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 
@@ -155,7 +155,7 @@
         }
 
         final int numPreferredLocales = preferredLocales.size();
-        final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
+        final ArrayMap<String, ScoreEntry> scoreboard = new ArrayMap<>();
         final byte[] score = new byte[numPreferredLocales];
         final ULocale[] preferredULocaleCache = new ULocale[numPreferredLocales];
 
@@ -197,7 +197,11 @@
             }
         }
 
-        final ScoreEntry[] result = scoreboard.values().toArray(new ScoreEntry[scoreboard.size()]);
+        final int numEntries = scoreboard.size();
+        final ScoreEntry[] result = new ScoreEntry[numEntries];
+        for (int i = 0; i < numEntries; ++i) {
+            result[i] = scoreboard.valueAt(i);
+        }
         Arrays.sort(result);
         for (final ScoreEntry entry : result) {
             dest.add(sources.get(entry.mIndex));
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 9d402b3..c18a79f 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -16,10 +16,7 @@
 
 package com.android.server.media;
 
-import static android.media.SessionToken2.TYPE_SESSION;
-
 import android.app.ActivityManager;
-import android.app.AppGlobals;
 import android.app.INotificationManager;
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
@@ -30,7 +27,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
@@ -40,9 +36,6 @@
 import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.IRemoteVolumeController;
-import android.media.ISessionTokensListener;
-import android.media.MediaController2;
-import android.media.SessionToken2;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -68,7 +61,6 @@
 import android.provider.Settings;
 import android.speech.RecognizerIntent;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -85,15 +77,12 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
 
 /**
  * System implementation of MediaSessionManager
  */
 public class MediaSessionService extends SystemService implements Monitor {
     private static final String TAG = "MediaSessionService";
-    static final boolean USE_MEDIA2_APIS = false; // TODO: Change this to true when we're ready.
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     // Leave log for key event always.
     private static final boolean DEBUG_KEY_EVENT = true;
@@ -113,7 +102,6 @@
     private final PowerManager.WakeLock mMediaEventWakeLock;
     private final int mLongPressTimeout;
     private final INotificationManager mNotificationManager;
-    private final IPackageManager mPackageManager;
 
     private KeyguardManager mKeyguardManager;
     private IAudioService mAudioService;
@@ -131,13 +119,6 @@
     // better way to handle this.
     private IRemoteVolumeController mRvc;
 
-    // MediaSession2 support
-    // TODO(jaewan): Support multi-user and managed profile. (b/73597722)
-    // TODO(jaewan): Make it priority list for handling volume/media key. (b/73760382)
-    private final Map<SessionToken2, MediaController2> mSessionRecords = new ArrayMap<>();
-
-    private final List<SessionTokensListenerRecord> mSessionTokensListeners = new ArrayList<>();
-
     public MediaSessionService(Context context) {
         super(context);
         mSessionManagerImpl = new SessionManagerImpl();
@@ -146,7 +127,6 @@
         mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
         mNotificationManager = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-        mPackageManager = AppGlobals.getPackageManager();
     }
 
     @Override
@@ -645,20 +625,6 @@
         return mUserRecords.get(fullUserId);
     }
 
-    void destroySession2Internal(SessionToken2 token) {
-        synchronized (mLock) {
-            boolean notifySessionTokensUpdated = false;
-            if (token.getType() == SessionToken2.TYPE_SESSION) {
-                notifySessionTokensUpdated |= removeSessionRecordLocked(token);
-            } else {
-                notifySessionTokensUpdated |= addSessionRecordLocked(token);
-            }
-            if (notifySessionTokensUpdated) {
-                postSessionTokensUpdated(UserHandle.getUserId(token.getUid()));
-            }
-        }
-    }
-
     /**
      * Information about a full user and its corresponding managed profiles.
      *
@@ -1417,163 +1383,6 @@
             }
         }
 
-        /**
-         * Called when a {@link android.media.MediaSession2} instance is created.
-         * <p>
-         * This does two things.
-         *   1. Keep the newly created session in the service
-         *   2. Do sanity check to ensure unique id per package, and return result
-         *
-         * @param sessionToken SessionToken2 object in bundled form
-         * @return {@code true} if the session's id isn't used by the package now. {@code false}
-         *     otherwise.
-         */
-        @Override
-        public boolean createSession2(Bundle sessionToken) {
-            if (!USE_MEDIA2_APIS) {
-                return false;
-            }
-            final int uid = Binder.getCallingUid();
-            final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
-            if (token == null || token.getUid() != uid) {
-                Log.w(TAG, "onSessionCreated failed, expected caller uid=" + token.getUid()
-                        + " but from uid=" + uid);
-            }
-            if (DEBUG) {
-                Log.d(TAG, "createSession2: " + token);
-            }
-            synchronized (mLock) {
-                MediaController2 controller = mSessionRecords.get(token);
-                if (controller != null && controller.isConnected()) {
-                    return false;
-                }
-                Context context = getContext();
-                controller = new MediaController2(context, token, context.getMainExecutor(),
-                        new ControllerCallback(token));
-                if (addSessionRecordLocked(token, controller)) {
-                    postSessionTokensUpdated(UserHandle.getUserId(token.getUid()));
-                }
-                return true;
-            }
-        }
-
-        /**
-         * Called when a {@link android.media.MediaSession2} instance is closed. (i.e. destroyed)
-         * <p>
-         * Ideally service should know that a session is destroyed through the
-         * {@link android.media.MediaController2.ControllerCallback#onDisconnected()}, which is
-         * asynchronous call. However, we also need synchronous way together to address timing
-         * issue. If the package recreates the session almost immediately, which happens commonly
-         * for tests, service will reject the creation through {@link #onSessionCreated(Bundle)}
-         * if the service hasn't notified previous destroy yet. This synchronous API will address
-         * the issue.
-         *
-         * @param sessionToken SessionToken2 object in bundled form
-         */
-        @Override
-        public void destroySession2(Bundle sessionToken) {
-            if (!USE_MEDIA2_APIS) {
-                return;
-            }
-            final int uid = Binder.getCallingUid();
-            final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
-            if (token == null || token.getUid() != uid) {
-                Log.w(TAG, "onSessionDestroyed failed, expected caller uid=" + token.getUid()
-                        + " but from uid=" + uid);
-            }
-            if (DEBUG) {
-                Log.d(TAG, "destroySession2 " + token);
-            }
-            destroySession2Internal(token);
-        }
-
-        // TODO(jaewan): Make this API take userId as an argument (b/73597722)
-        @Override
-        public List<Bundle> getSessionTokens(boolean activeSessionOnly,
-                boolean sessionServiceOnly, String packageName) throws RemoteException {
-            if (!USE_MEDIA2_APIS) {
-                return null;
-            }
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            List<Bundle> tokens = new ArrayList<>();
-            try {
-                verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
-                synchronized (mLock) {
-                    for (Map.Entry<SessionToken2, MediaController2> record
-                            : mSessionRecords.entrySet()) {
-                        boolean isSessionService = (record.getKey().getType() != TYPE_SESSION);
-                        boolean isActive = record.getValue() != null;
-                        if ((activeSessionOnly && !isActive)
-                                || (sessionServiceOnly && !isSessionService)) {
-                            continue;
-                        }
-                        tokens.add(record.getKey().toBundle());
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            return tokens;
-        }
-
-        @Override
-        public void addSessionTokensListener(ISessionTokensListener listener, int userId,
-                String packageName) throws RemoteException {
-            if (!USE_MEDIA2_APIS) {
-                return;
-            }
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                int resolvedUserId = verifySessionsRequest2(userId, packageName, pid, uid);
-                synchronized (mLock) {
-                    final SessionTokensListenerRecord record =
-                            new SessionTokensListenerRecord(listener, resolvedUserId);
-                    try {
-                        listener.asBinder().linkToDeath(record, 0);
-                    } catch (RemoteException e) {
-                    }
-                    mSessionTokensListeners.add(record);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        // TODO(jaewan): Make this API take userId as an argument (b/73597722)
-        @Override
-        public void removeSessionTokensListener(ISessionTokensListener listener,
-                String packageName) throws RemoteException {
-            if (!USE_MEDIA2_APIS) {
-                return;
-            }
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
-                synchronized (mLock) {
-                    IBinder listenerBinder = listener.asBinder();
-                    for (SessionTokensListenerRecord record : mSessionTokensListeners) {
-                        if (listenerBinder.equals(record.mListener.asBinder())) {
-                            try {
-                                listenerBinder.unlinkToDeath(record, 0);
-                            } catch (NoSuchElementException e) {
-                            }
-                            mSessionTokensListeners.remove(record);
-                            break;
-                        }
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
         // For MediaSession
         private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
                 final int uid) {
@@ -1594,23 +1403,6 @@
             return resolvedUserId;
         }
 
-        // For MediaSession2
-        private int verifySessionsRequest2(int targetUserId, String callerPackageName,
-                int callerPid, int callerUid) throws RemoteException {
-            // Check that they can make calls on behalf of the user and get the final user id.
-            int resolvedUserId = ActivityManager.handleIncomingUser(callerPid, callerUid,
-                    targetUserId, true /* allowAll */, true /* requireFull */, "getSessionTokens",
-                    callerPackageName);
-            // Check if they have the permissions or their component is
-            // enabled for the user they're calling from.
-            if (!hasMediaControlPermission(
-                    resolvedUserId, callerPackageName, callerPid, callerUid)) {
-                throw new SecurityException("Missing permission to control media.");
-            }
-            return resolvedUserId;
-        }
-
-        // For MediaSession2
         private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
                 int pid, int uid) throws RemoteException {
             // Allow API calls from the System UI
@@ -2014,7 +1806,6 @@
     final class MessageHandler extends Handler {
         private static final int MSG_SESSIONS_CHANGED = 1;
         private static final int MSG_VOLUME_INITIAL_DOWN = 2;
-        private static final int MSG_SESSIONS_TOKENS_CHANGED = 3;
         private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
 
         @Override
@@ -2033,9 +1824,6 @@
                         }
                     }
                     break;
-                case MSG_SESSIONS_TOKENS_CHANGED:
-                    pushSessionTokensChanged((int) msg.obj);
-                    break;
             }
         }
 
@@ -2050,86 +1838,4 @@
             obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
         }
     }
-
-    private class ControllerCallback extends MediaController2.ControllerCallback {
-
-        private final SessionToken2 mToken;
-
-        ControllerCallback(SessionToken2 token) {
-            mToken = token;
-        }
-
-        @Override
-        public void onDisconnected(MediaController2 controller) {
-            destroySession2Internal(mToken);
-        }
-    };
-
-    private final class SessionTokensListenerRecord implements IBinder.DeathRecipient {
-        private final ISessionTokensListener mListener;
-        private final int mUserId;
-
-        public SessionTokensListenerRecord(ISessionTokensListener listener, int userId) {
-            mListener = listener;
-            // TODO(jaewan): should userId be mapped through mFullUserIds? (b/73597722)
-            mUserId = userId;
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mLock) {
-                mSessionTokensListeners.remove(this);
-            }
-        }
-    }
-
-    private void postSessionTokensUpdated(int userId) {
-        mHandler.obtainMessage(MessageHandler.MSG_SESSIONS_TOKENS_CHANGED, userId).sendToTarget();
-    }
-
-    private void pushSessionTokensChanged(int userId) {
-        synchronized (mLock) {
-            List<Bundle> tokens = new ArrayList<>();
-            for (SessionToken2 token : mSessionRecords.keySet()) {
-                if (UserHandle.getUserId(token.getUid()) == userId) {
-                    tokens.add(token.toBundle());
-                }
-            }
-
-            for (SessionTokensListenerRecord record : mSessionTokensListeners) {
-                // TODO(jaewan): Should userId be mapped through mFullUserIds? (b/73760382)
-                if (record.mUserId == userId || record.mUserId == UserHandle.USER_ALL) {
-                    try {
-                        record.mListener.onSessionTokensChanged(tokens);
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to notify session tokens changed", e);
-                    }
-                }
-            }
-        }
-    }
-
-    private boolean addSessionRecordLocked(SessionToken2 token) {
-        return addSessionRecordLocked(token, null);
-    }
-
-    private boolean addSessionRecordLocked(SessionToken2 token, MediaController2 controller) {
-        if (mSessionRecords.containsKey(token) && mSessionRecords.get(token) == controller) {
-            // The key/value pair already exists, no need to update.
-            return false;
-        }
-
-        mSessionRecords.put(token, controller);
-        return true;
-    }
-
-    private boolean removeSessionRecordLocked(SessionToken2 token) {
-        if (!mSessionRecords.containsKey(token)) {
-            // The key is already removed, no need to remove.
-            return false;
-        }
-
-        mSessionRecords.remove(token);
-        return true;
-    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 32990ce..4da29e4 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -411,7 +411,7 @@
     private NotificationAssistants mAssistants;
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
-    private boolean mLockScreenAllowSecureNotifications;
+    private boolean mLockScreenAllowSecureNotifications = true;
 
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 5810e30..b52c021 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -333,9 +333,7 @@
         PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
                 collectingInstaller, mPackageManagerService.mInstallLock, mContext);
 
-        String[] libraryDependencies = pkg.usesLibraryFiles;
-
-        optimizer.performDexOpt(pkg, libraryDependencies,
+        optimizer.performDexOpt(pkg, pkg.usesLibraryInfos,
                 null /* ISAs */,
                 null /* CompilerStats.PackageStats */,
                 mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 95d2154..cc640f0 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.DexMetadataHelper;
 import android.os.FileUtils;
@@ -129,7 +130,7 @@
      * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
      * synchronized on {@link #mInstallLock}.
      */
-    int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
+    int performDexOpt(PackageParser.Package pkg, List<SharedLibraryInfo> sharedLibraries,
             String[] instructionSets, CompilerStats.PackageStats packageStats,
             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
         if (pkg.applicationInfo.uid == -1) {
@@ -155,7 +156,8 @@
      * It assumes the install lock is held.
      */
     @GuardedBy("mInstallLock")
-    private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
+    private int performDexOptLI(PackageParser.Package pkg,
+            List<SharedLibraryInfo> sharedLibraries,
             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
         final String[] instructionSets = targetInstructionSets != null ?
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9856a2b..acbd81d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2233,7 +2233,7 @@
             for (int i = 0; i < builtInLibCount; i++) {
                 String name = libConfig.keyAt(i);
                 String path = libConfig.valueAt(i);
-                addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
+                addSharedLibraryLPw(path, null, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
                         SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
             }
             // Builtin libraries cannot encode their dependency where they are
@@ -4844,7 +4844,8 @@
                     }
 
                     SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(),
-                            libInfo.getPackageName(), libInfo.getName(), libInfo.getLongVersion(),
+                            libInfo.getPackageName(), libInfo.getAllCodePaths(),
+                            libInfo.getName(), libInfo.getLongVersion(),
                             libInfo.getType(), libInfo.getDeclaringPackage(),
                             getPackagesUsingSharedLibraryLPr(libInfo, flags, userId),
                             (libInfo.getDependencies() == null
@@ -9225,7 +9226,7 @@
                     mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
             }
         }
-        return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
+        return pdo.performDexOpt(p, p.usesLibraryInfos, instructionSets,
                 getOrCreateCompilerPackageStats(p),
                 mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
     }
@@ -9628,7 +9629,6 @@
     @GuardedBy("mPackages")
     private void addSharedLibraryLPr(PackageParser.Package pkg, Set<String> usesLibraryFiles,
             SharedLibraryInfo libInfo, PackageParser.Package changingLib) {
-
         if (libInfo.getPath() != null) {
             usesLibraryFiles.add(libInfo.getPath());
             return;
@@ -11224,8 +11224,9 @@
         }
     }
 
-    private boolean addSharedLibraryLPw(String path, String apk, String name, long version,
-            int type, String declaringPackageName, long declaringVersionCode) {
+    private boolean addSharedLibraryLPw(String path, String apk, List<String> codePaths,
+            String name, long version, int type, String declaringPackageName,
+            long declaringVersionCode) {
         LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             versionedLib = new LongSparseArray<>();
@@ -11236,7 +11237,7 @@
         } else if (versionedLib.indexOfKey(version) >= 0) {
             return false;
         }
-        SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, name,
+        SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, codePaths, name,
                 version, type, new VersionedPackage(declaringPackageName, declaringVersionCode),
                 null, null);
         versionedLib.put(version, libraryInfo);
@@ -11323,10 +11324,17 @@
             if (pkg.staticSharedLibName != null) {
                 // Static shared libs don't allow renaming as they have synthetic package
                 // names to allow install of multiple versions, so use name from manifest.
-                if (addSharedLibraryLPw(null, pkg.packageName, pkg.staticSharedLibName,
+                if (addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(),
+                        pkg.staticSharedLibName,
                         pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC,
                         pkg.manifestPackageName, pkg.getLongVersionCode())) {
                     hasStaticSharedLibs = true;
+                    // Shared libraries for the package need to be updated.
+                    try {
+                        updateSharedLibrariesLPr(pkg, null);
+                    } catch (PackageManagerException e) {
+                        Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
+                    }
                 } else {
                     Slog.w(TAG, "Package " + pkg.packageName + " library "
                                 + pkg.staticSharedLibName + " already exists; skipping");
@@ -11368,13 +11376,19 @@
                             allowed = true;
                         }
                         if (allowed) {
-                            if (!addSharedLibraryLPw(null, pkg.packageName, name,
-                                    SharedLibraryInfo.VERSION_UNDEFINED,
+                            if (!addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(),
+                                    name, SharedLibraryInfo.VERSION_UNDEFINED,
                                     SharedLibraryInfo.TYPE_DYNAMIC,
                                     pkg.packageName, pkg.getLongVersionCode())) {
                                 Slog.w(TAG, "Package " + pkg.packageName + " library "
                                         + name + " already exists; skipping");
                             }
+                            // Shared libraries for the package need to be updated.
+                            try {
+                                updateSharedLibrariesLPr(pkg, null);
+                            } catch (PackageManagerException e) {
+                                Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
+                            }
                         } else {
                             Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
                                     + name + " that is not declared on system image; skipping");
@@ -15617,7 +15631,7 @@
                         REASON_INSTALL,
                         DexoptOptions.DEXOPT_BOOT_COMPLETE
                                 | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
-                mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
+                mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryInfos,
                         null /* instructionSets */,
                         getOrCreateCompilerPackageStats(pkg),
                         mDexManager.getPackageUseInfoOrDefault(packageName),
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index 9a12a2f..93ee44c 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -17,6 +17,7 @@
 package com.android.server.pm.dex;
 
 import android.content.pm.ApplicationInfo;
+import android.content.pm.SharedLibraryInfo;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -29,6 +30,11 @@
 public final class DexoptUtils {
     private static final String TAG = "DexoptUtils";
 
+    // Shared libraries have more or less followed PCL behavior due to the way
+    // they were added to the classpath pre Q.
+    private static final String SHARED_LIBRARY_LOADER_TYPE =
+            ClassLoaderFactory.getPathClassLoaderName();
+
     private DexoptUtils() {}
 
     /**
@@ -62,12 +68,15 @@
      * android.app.ActivityThread, boolean, ApplicationInfo, List, List)}.
      */
     public static String[] getClassLoaderContexts(ApplicationInfo info,
-            String[] sharedLibraries, boolean[] pathsWithCode) {
+            List<SharedLibraryInfo> sharedLibraries, boolean[] pathsWithCode) {
         // The base class loader context contains only the shared library.
-        String sharedLibrariesClassPath = encodeClasspath(sharedLibraries);
-        String baseApkContextClassLoader = encodeClassLoader(
-                sharedLibrariesClassPath, info.classLoaderName);
+        String sharedLibrariesContext = "";
+        if (sharedLibraries != null) {
+            sharedLibrariesContext = encodeSharedLibraries(sharedLibraries);
+        }
 
+        String baseApkContextClassLoader = encodeClassLoader(
+                "", info.classLoaderName, sharedLibrariesContext);
         if (info.getSplitCodePaths() == null) {
             // The application has no splits.
             return new String[] {baseApkContextClassLoader};
@@ -81,11 +90,10 @@
         // The splits have an implicit dependency on the base apk.
         // This means that we have to add the base apk file in addition to the shared libraries.
         String baseApkName = new File(info.getBaseCodePath()).getName();
-        String sharedLibrariesAndBaseClassPath =
-                encodeClasspath(sharedLibrariesClassPath, baseApkName);
+        String baseClassPath = baseApkName;
 
         // The result is stored in classLoaderContexts.
-        // Index 0 is the class loaded context for the base apk.
+        // Index 0 is the class loader context for the base apk.
         // Index `i` is the class loader context encoding for split `i`.
         String[] classLoaderContexts = new String[/*base apk*/ 1 + splitRelativeCodePaths.length];
         classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null;
@@ -94,10 +102,14 @@
             // If the app didn't request for the splits to be loaded in isolation or if it does not
             // declare inter-split dependencies, then all the splits will be loaded in the base
             // apk class loader (in the order of their definition).
-            String classpath = sharedLibrariesAndBaseClassPath;
+            String classpath = baseClassPath;
             for (int i = 1; i < classLoaderContexts.length; i++) {
-                classLoaderContexts[i] = pathsWithCode[i]
-                        ? encodeClassLoader(classpath, info.classLoaderName) : null;
+                if (pathsWithCode[i]) {
+                    classLoaderContexts[i] = encodeClassLoader(
+                            classpath, info.classLoaderName, sharedLibrariesContext);
+                } else {
+                    classLoaderContexts[i] = null;
+                }
                 // Note that the splits with no code are not removed from the classpath computation.
                 // i.e. split_n might get the split_n-1 in its classpath dependency even
                 // if split_n-1 has no code.
@@ -124,7 +136,7 @@
                         info.splitClassLoaderNames[i]);
             }
             String splitDependencyOnBase = encodeClassLoader(
-                    sharedLibrariesAndBaseClassPath, info.classLoaderName);
+                    baseClassPath, info.classLoaderName);
             SparseArray<int[]> splitDependencies = info.splitDependencies;
 
             // Note that not all splits have dependencies (e.g. configuration splits)
@@ -149,7 +161,8 @@
                     // any dependency. In this case its context equals its declared class loader.
                     classLoaderContexts[i] = classLoaderContexts[i] == null
                             ? splitClassLoader
-                            : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i]);
+                            : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i])
+                                    + sharedLibrariesContext;
                 } else {
                     // This is a split without code, it has no dependency and it is not compiled.
                     // Its context will be null.
@@ -207,6 +220,31 @@
         return splitContext;
     }
 
+    private static String encodeSharedLibrary(SharedLibraryInfo sharedLibrary) {
+        List<String> paths = sharedLibrary.getAllCodePaths();
+        String classLoaderSpec = encodeClassLoader(
+                encodeClasspath(paths.toArray(new String[paths.size()])),
+                SHARED_LIBRARY_LOADER_TYPE);
+        if (sharedLibrary.getDependencies() != null) {
+            classLoaderSpec += encodeSharedLibraries(sharedLibrary.getDependencies());
+        }
+        return classLoaderSpec;
+    }
+
+    private static String encodeSharedLibraries(List<SharedLibraryInfo> sharedLibraries) {
+        String sharedLibrariesContext = "{";
+        boolean first = true;
+        for (SharedLibraryInfo info : sharedLibraries) {
+            if (!first) {
+                sharedLibrariesContext += "#";
+            }
+            first = false;
+            sharedLibrariesContext += encodeSharedLibrary(info);
+        }
+        sharedLibrariesContext += "}";
+        return sharedLibrariesContext;
+    }
+
     /**
      * Encodes the shared libraries classpathElements in a format accepted by dexopt.
      * NOTE: Keep this in sync with the dexopt expectations! Right now that is
@@ -258,6 +296,14 @@
     }
 
     /**
+     * Same as above, but appends {@param sharedLibraries} to the result.
+     */
+    private static String encodeClassLoader(String classpath, String classLoaderName,
+            String sharedLibraries) {
+        return encodeClassLoader(classpath, classLoaderName) + sharedLibraries;
+    }
+
+    /**
      * Links to dependencies together in a format accepted by dexopt.
      * For the special case when either of cl1 or cl2 equals
      * {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns the same. This
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 59c6d0a..3ba1155 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -20,13 +20,7 @@
 import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.AppOpsManager.OP_TOAST_WINDOW;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Context.CONTEXT_RESTRICTED;
 import static android.content.Context.DISPLAY_SERVICE;
 import static android.content.Context.WINDOW_SERVICE;
@@ -35,61 +29,28 @@
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.res.Configuration.EMPTY;
-import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
-import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.os.Build.VERSION_CODES.M;
 import static android.os.Build.VERSION_CODES.O;
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.STATE_OFF;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
-import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 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_BOOT_PROGRESS;
 import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -106,19 +67,13 @@
 import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
@@ -132,25 +87,15 @@
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
-import static com.android.server.wm.WindowManagerPolicyProto.FOCUSED_APP_TOKEN;
-import static com.android.server.wm.WindowManagerPolicyProto.FOCUSED_WINDOW;
-import static com.android.server.wm.WindowManagerPolicyProto.FORCE_STATUS_BAR;
-import static com.android.server.wm.WindowManagerPolicyProto.FORCE_STATUS_BAR_FROM_KEYGUARD;
 import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
 import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DRAW_COMPLETE;
 import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED;
 import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_CHANGED;
 import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_PENDING;
-import static com.android.server.wm.WindowManagerPolicyProto.LAST_SYSTEM_UI_FLAGS;
-import static com.android.server.wm.WindowManagerPolicyProto.NAVIGATION_BAR;
 import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION;
-import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION_LISTENER;
 import static com.android.server.wm.WindowManagerPolicyProto.ROTATION;
 import static com.android.server.wm.WindowManagerPolicyProto.ROTATION_MODE;
 import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY;
-import static com.android.server.wm.WindowManagerPolicyProto.STATUS_BAR;
-import static com.android.server.wm.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW;
-import static com.android.server.wm.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_WINDOW;
 import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;
 
 import android.annotation.Nullable;
@@ -158,12 +103,10 @@
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
-import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.app.IUiModeManager;
 import android.app.ProgressDialog;
 import android.app.SearchManager;
-import android.app.StatusBarManager;
 import android.app.UiModeManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -181,15 +124,12 @@
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
-import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerInternal;
-import android.hardware.power.V1_0.PowerHint;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
@@ -202,7 +142,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IDeviceIdleController;
-import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
@@ -224,7 +163,6 @@
 import android.service.vr.IPersistentVrStateCallbacks;
 import android.speech.RecognizerIntent;
 import android.telecom.TelecomManager;
-import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.LongSparseArray;
@@ -234,21 +172,13 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
-import android.view.DisplayCutout;
-import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
-import android.view.IApplicationToken;
 import android.view.IWindowManager;
-import android.view.InputChannel;
 import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
 import android.view.KeyCharacterMap;
 import android.view.KeyCharacterMap.FallbackAction;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
-import android.view.PointerIcon;
-import android.view.Surface;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
@@ -263,8 +193,6 @@
 
 import com.android.internal.R;
 import com.android.internal.accessibility.AccessibilityShortcutController;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -272,7 +200,6 @@
 import com.android.internal.policy.PhoneWindow;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ScreenShapeHelper;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.widget.PointerLocationView;
 import com.android.server.ExtconStateObserver;
@@ -289,19 +216,17 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
 import com.android.server.wm.AppTransition;
-import com.android.server.wm.DisplayFrames;
 import com.android.server.wm.DisplayPolicy;
 import com.android.server.wm.DisplayRotation;
-import com.android.server.wm.WindowFrames;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-import com.android.server.wm.utils.InsetUtils;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.HashSet;
 import java.util.List;
 
 /**
@@ -313,11 +238,9 @@
  */
 public class PhoneWindowManager implements WindowManagerPolicy {
     static final String TAG = "WindowManager";
-    static final boolean DEBUG = false;
     static final boolean localLOGV = false;
     static final boolean DEBUG_INPUT = false;
     static final boolean DEBUG_KEYGUARD = false;
-    static final boolean DEBUG_LAYOUT = false;
     static final boolean DEBUG_SPLASH_SCREEN = false;
     static final boolean DEBUG_WAKEUP = false;
     static final boolean SHOW_SPLASH_SCREENS = true;
@@ -329,8 +252,6 @@
     // Whether to allow devices placed in vr headset viewers to have an alternative Home intent.
     static final boolean ENABLE_VR_HEADSET_HOME_CAPTURE = true;
 
-    static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false;
-
     static final int SHORT_PRESS_POWER_NOTHING = 0;
     static final int SHORT_PRESS_POWER_GO_TO_SLEEP = 1;
     static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2;
@@ -372,13 +293,6 @@
 
     static final int PENDING_KEY_NULL = -1;
 
-    // Controls navigation bar opacity depending on which workspace stacks are currently
-    // visible.
-    // Nav bar is always opaque when either the freeform stack or docked stack is visible.
-    static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0;
-    // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque.
-    static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 1;
-
     static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
     static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
     static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
@@ -386,33 +300,11 @@
     static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
     static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
 
-    /**
-     * These are the system UI flags that, when changing, can cause the layout
-     * of the screen to change.
-     */
-    static final int SYSTEM_UI_CHANGING_LAYOUT =
-              View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
-            | View.SYSTEM_UI_FLAG_FULLSCREEN
-            | View.STATUS_BAR_TRANSLUCENT
-            | View.NAVIGATION_BAR_TRANSLUCENT
-            | View.STATUS_BAR_TRANSPARENT
-            | View.NAVIGATION_BAR_TRANSPARENT;
-
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
             .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
             .build();
 
-    // The panic gesture may become active only after the keyguard is dismissed and the immersive
-    // app shows again. If that doesn't happen for 30s we drop the gesture.
-    private static final long PANIC_GESTURE_EXPIRATION = 30000;
-
-    private static final String SYSUI_PACKAGE = "com.android.systemui";
-    private static final String SYSUI_SCREENSHOT_SERVICE =
-            "com.android.systemui.screenshot.TakeScreenshotService";
-    private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
-            "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
-
     /**
      * Keyguard stuff
      */
@@ -501,16 +393,7 @@
     private AccessibilityShortcutController mAccessibilityShortcutController;
 
     boolean mSafeMode;
-    private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>();
-    WindowState mStatusBar = null;
-    private final int[] mStatusBarHeightForRotation = new int[4];
-    WindowState mNavigationBar = null;
-    @NavigationBarPosition
-    int mNavigationBarPosition = NAV_BAR_BOTTOM;
-    int[] mNavigationBarHeightForRotationDefault = new int[4];
-    int[] mNavigationBarWidthForRotationDefault = new int[4];
-    int[] mNavigationBarHeightForRotationInCarMode = new int[4];
-    int[] mNavigationBarWidthForRotationInCarMode = new int[4];
+    private WindowState mKeyguardCandidate = null;
 
     private LongSparseArray<IShortcutService> mShortcutKeyServices = new LongSparseArray<>();
 
@@ -591,7 +474,6 @@
     int mShortPressOnSleepBehavior;
     int mShortPressOnWindowBehavior;
     boolean mHasSoftInput = false;
-    boolean mTranslucentDecorEnabled = true;
     boolean mUseTvRouting;
     int mVeryLongPressTimeout;
     boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
@@ -600,74 +482,12 @@
     private boolean mHandleVolumeKeysInWM;
 
     int mPointerLocationMode = 0; // guarded by mLock
-
-    // The windows we were told about in focusChanged.
-    WindowState mFocusedWindow;
-    WindowState mLastFocusedWindow;
-
-    IApplicationToken mFocusedApp;
-
     PointerLocationView mPointerLocationView;
 
-    // During layout, the layer at which the doc window is placed.
-    int mDockLayer;
-    // During layout, this is the layer of the status bar.
-    int mStatusBarLayer;
-    int mLastSystemUiFlags;
-    // Bits that we are in the process of clearing, so we want to prevent
-    // them from being set by applications until everything has been updated
-    // to have them clear.
-    int mResettingSystemUiFlags = 0;
-    // Bits that we are currently always keeping cleared.
-    int mForceClearedSystemUiFlags = 0;
-    int mLastFullscreenStackSysUiFlags;
-    int mLastDockedStackSysUiFlags;
-    final Rect mNonDockedStackBounds = new Rect();
-    final Rect mDockedStackBounds = new Rect();
-    final Rect mLastNonDockedStackBounds = new Rect();
-    final Rect mLastDockedStackBounds = new Rect();
-
-    // What we last reported to system UI about whether the compatibility
-    // menu needs to be displayed.
-    boolean mLastFocusNeedsMenu = false;
-    // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
-    private long mPendingPanicGestureUptime;
-
-    InputConsumer mInputConsumer = null;
-
-    private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
-    private static final Rect sTmpRect = new Rect();
-    private static final Rect sTmpDockedFrame = new Rect();
-    private static final Rect sTmpNavFrame = new Rect();
-    private static final Rect sTmpLastParentFrame = new Rect();
-
-    WindowState mTopFullscreenOpaqueWindowState;
-    WindowState mTopFullscreenOpaqueOrDimmingWindowState;
-    WindowState mTopDockedOpaqueWindowState;
-    WindowState mTopDockedOpaqueOrDimmingWindowState;
-    boolean mTopIsFullscreen;
-    boolean mForceStatusBar;
-    boolean mForceStatusBarFromKeyguard;
-    private boolean mForceStatusBarTransparent;
-    int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
-    boolean mForcingShowNavBar;
-    int mForcingShowNavBarLayer;
-
     private boolean mPendingKeyguardOccluded;
     private boolean mKeyguardOccludedChanged;
     private boolean mNotifyUserActivity;
 
-    boolean mShowingDream;
-    private boolean mLastShowingDream;
-    boolean mDreamingLockscreen;
-    boolean mDreamingSleepTokenNeeded;
-    private boolean mWindowSleepTokenNeeded;
-    private boolean mLastWindowSleepTokenNeeded;
-
-    @GuardedBy("mHandler")
-    private SleepToken mWindowSleepToken;
-
-    SleepToken mDreamingSleepToken;
     SleepToken mScreenOffSleepToken;
     volatile boolean mKeyguardOccluded;
     Intent mHomeIntent;
@@ -680,10 +500,9 @@
     boolean mPendingCapsLockToggle;
     int mMetaState;
     int mInitialMetaState;
-    boolean mForceShowSystemBars;
 
     // support for activating the lock screen while the screen is on
-    boolean mAllowLockscreenWhenOn;
+    private HashSet<Integer> mAllowLockscreenWhenOnDisplays = new HashSet<>();
     int mLockScreenTimeout;
     boolean mLockScreenTimerActive;
 
@@ -704,7 +523,6 @@
     Display mDefaultDisplay;
     DisplayRotation mDefaultDisplayRotation;
     DisplayPolicy mDefaultDisplayPolicy;
-    WindowOrientationListener mDefaultOrientationListener;
 
     // What we do when the user long presses on home
     private int mLongPressOnHomeBehavior;
@@ -781,10 +599,6 @@
 
     private boolean mAodShowing;
 
-    // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
-    private NavigationBarExperiments mExperiments = new NavigationBarExperiments();
-    // EXPERIMENT END
-
     private static final int MSG_ENABLE_POINTER_LOCATION = 1;
     private static final int MSG_DISABLE_POINTER_LOCATION = 2;
     private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
@@ -798,25 +612,19 @@
     private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12;
     private static final int MSG_POWER_DELAYED_PRESS = 13;
     private static final int MSG_POWER_LONG_PRESS = 14;
-    private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 15;
-    private static final int MSG_REQUEST_TRANSIENT_BARS = 16;
-    private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 17;
-    private static final int MSG_BACK_LONG_PRESS = 18;
-    private static final int MSG_DISPOSE_INPUT_CONSUMER = 19;
-    private static final int MSG_ACCESSIBILITY_SHORTCUT = 20;
-    private static final int MSG_BUGREPORT_TV = 21;
-    private static final int MSG_ACCESSIBILITY_TV = 22;
-    private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 23;
-    private static final int MSG_SYSTEM_KEY_PRESS = 24;
-    private static final int MSG_HANDLE_ALL_APPS = 25;
-    private static final int MSG_LAUNCH_ASSIST = 26;
-    private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 27;
-    private static final int MSG_POWER_VERY_LONG_PRESS = 28;
-    private static final int MSG_NOTIFY_USER_ACTIVITY = 29;
-    private static final int MSG_RINGER_TOGGLE_CHORD = 30;
-
-    private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
-    private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
+    private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15;
+    private static final int MSG_BACK_LONG_PRESS = 16;
+    private static final int MSG_ACCESSIBILITY_SHORTCUT = 17;
+    private static final int MSG_BUGREPORT_TV = 18;
+    private static final int MSG_ACCESSIBILITY_TV = 19;
+    private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 20;
+    private static final int MSG_SYSTEM_KEY_PRESS = 21;
+    private static final int MSG_HANDLE_ALL_APPS = 22;
+    private static final int MSG_LAUNCH_ASSIST = 23;
+    private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 24;
+    private static final int MSG_POWER_VERY_LONG_PRESS = 25;
+    private static final int MSG_NOTIFY_USER_ACTIVITY = 26;
+    private static final int MSG_RINGER_TOGGLE_CHORD = 27;
 
     private class PolicyHandler extends Handler {
         @Override
@@ -876,25 +684,12 @@
                 case MSG_POWER_VERY_LONG_PRESS:
                     powerVeryLongPress();
                     break;
-                case MSG_UPDATE_DREAMING_SLEEP_TOKEN:
-                    updateDreamingSleepToken(msg.arg1 != 0);
-                    break;
-                case MSG_REQUEST_TRANSIENT_BARS:
-                    WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS) ?
-                            mStatusBar : mNavigationBar;
-                    if (targetBar != null) {
-                        requestTransientBars(targetBar);
-                    }
-                    break;
                 case MSG_SHOW_PICTURE_IN_PICTURE_MENU:
                     showPictureInPictureMenuInternal();
                     break;
                 case MSG_BACK_LONG_PRESS:
                     backLongPress();
                     break;
-                case MSG_DISPOSE_INPUT_CONSUMER:
-                    disposeInputConsumer((InputConsumer) msg.obj);
-                    break;
                 case MSG_ACCESSIBILITY_SHORTCUT:
                     accessibilityShortcutActivated();
                     break;
@@ -966,14 +761,8 @@
                     Settings.Secure.DEFAULT_INPUT_METHOD), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.VOLUME_HUSH_GESTURE), false, this,
                     UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.POLICY_CONTROL), false, this,
-                    UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED), false, this,
                     UserHandle.USER_ALL);
@@ -1012,45 +801,6 @@
         }
     };
 
-    private final StatusBarController mStatusBarController = new StatusBarController();
-
-    private final BarController mNavigationBarController = new BarController("NavigationBar",
-            View.NAVIGATION_BAR_TRANSIENT,
-            View.NAVIGATION_BAR_UNHIDE,
-            View.NAVIGATION_BAR_TRANSLUCENT,
-            StatusBarManager.WINDOW_NAVIGATION_BAR,
-            FLAG_TRANSLUCENT_NAVIGATION,
-            View.NAVIGATION_BAR_TRANSPARENT);
-
-    private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
-            new BarController.OnBarVisibilityChangedListener() {
-        @Override
-        public void onBarVisibilityChanged(boolean visible) {
-            mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible);
-        }
-    };
-
-    private final Runnable mAcquireSleepTokenRunnable = () -> {
-        if (mWindowSleepToken != null) {
-            return;
-        }
-        mWindowSleepToken = mActivityTaskManagerInternal.acquireSleepToken("WindowSleepToken",
-                DEFAULT_DISPLAY);
-    };
-
-    private final Runnable mReleaseSleepTokenRunnable = () -> {
-        if (mWindowSleepToken == null) {
-            return;
-        }
-        mWindowSleepToken.release();
-        mWindowSleepToken = null;
-    };
-
-    private ImmersiveModeConfirmation mImmersiveModeConfirmation;
-
-    @VisibleForTesting
-    SystemGesturesPointerEventListener mSystemGestures;
-
     private void handleRingerChordGesture() {
         if (mRingerToggleChord == VOLUME_HUSH_OFF) {
             return;
@@ -1150,14 +900,7 @@
             mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);
         }
 
-        // Detect user pressing the power button in panic when an application has
-        // taken over the whole screen.
-        boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
-                SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags),
-                isNavBarEmpty(mLastSystemUiFlags));
-        if (panic) {
-            mHandler.post(mHiddenNavPanic);
-        }
+        mWindowManagerFuncs.onPowerKeyDown(interactive);
 
         // Abort possibly stuck animations.
         mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
@@ -1500,12 +1243,6 @@
         mAccessibilityShortcutController.performAccessibilityShortcut();
     }
 
-    private void disposeInputConsumer(InputConsumer inputConsumer) {
-        if (inputConsumer != null) {
-            inputConsumer.dismiss();
-        }
-    }
-
     private void sleepPress() {
         if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
             launchHomeFromHotKey(DEFAULT_DISPLAY, false /* awakenDreams */,
@@ -1643,9 +1380,7 @@
 
         @Override
         public void run() {
-            mScreenshotHelper.takeScreenshot(mScreenshotType,
-                    mStatusBar != null && mStatusBar.isVisibleLw(),
-                    mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler);
+            mDefaultDisplayPolicy.takeScreenshot(mScreenshotType);
         }
     }
 
@@ -1673,7 +1408,8 @@
                 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0;
     }
 
-    boolean isUserSetupComplete() {
+    @Override
+    public boolean isUserSetupComplete() {
         boolean isSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
         if (mHasFeatureLeanback) {
@@ -1931,7 +1667,6 @@
         mDefaultDisplay = displayContentInfo.getDisplay();
         mDefaultDisplayRotation = displayContentInfo.getDisplayRotation();
         mDefaultDisplayPolicy = mDefaultDisplayRotation.getDisplayPolicy();
-        mDefaultOrientationListener = mDefaultDisplayRotation.getOrientationListener();
     }
 
     /** {@inheritDoc} */
@@ -2028,8 +1763,6 @@
                 com.android.internal.R.bool.config_lidControlsScreenLock);
         mLidControlsSleep = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_lidControlsSleep);
-        mTranslucentDecorEnabled = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_enableTranslucentDecor);
 
         mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
@@ -2107,76 +1840,6 @@
         filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
         context.registerReceiver(mMultiuserReceiver, filter);
 
-        // monitor for system gestures
-        // TODO(multi-display): Needs to be display specific.
-        mSystemGestures = new SystemGesturesPointerEventListener(context,
-                new SystemGesturesPointerEventListener.Callbacks() {
-                    @Override
-                    public void onSwipeFromTop() {
-                        if (mStatusBar != null) {
-                            requestTransientBars(mStatusBar);
-                        }
-                    }
-                    @Override
-                    public void onSwipeFromBottom() {
-                        if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) {
-                            requestTransientBars(mNavigationBar);
-                        }
-                    }
-                    @Override
-                    public void onSwipeFromRight() {
-                        if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) {
-                            requestTransientBars(mNavigationBar);
-                        }
-                    }
-                    @Override
-                    public void onSwipeFromLeft() {
-                        if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) {
-                            requestTransientBars(mNavigationBar);
-                        }
-                    }
-                    @Override
-                    public void onFling(int duration) {
-                        if (mPowerManagerInternal != null) {
-                            mPowerManagerInternal.powerHint(
-                                    PowerHint.INTERACTION, duration);
-                        }
-                    }
-                    @Override
-                    public void onDebug() {
-                        // no-op
-                    }
-                    @Override
-                    public void onDown() {
-                        mDefaultOrientationListener.onTouchStart();
-                    }
-                    @Override
-                    public void onUpOrCancel() {
-                        mDefaultOrientationListener.onTouchEnd();
-                    }
-                    @Override
-                    public void onMouseHoverAtTop() {
-                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
-                        Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
-                        msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS;
-                        mHandler.sendMessageDelayed(msg, 500);
-                    }
-                    @Override
-                    public void onMouseHoverAtBottom() {
-                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
-                        Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
-                        msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION;
-                        mHandler.sendMessageDelayed(msg, 500);
-                    }
-                    @Override
-                    public void onMouseLeaveFromEdge() {
-                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
-                    }
-                });
-        mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext);
-        //TODO (b/111365687) : make system context per display.
-        mWindowManagerFuncs.registerPointerEventListener(mSystemGestures, DEFAULT_DISPLAY);
-
         mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
         mLongPressVibePattern = getLongIntArray(mContext.getResources(),
                 com.android.internal.R.array.config_longPressVibePattern);
@@ -2199,8 +1862,6 @@
             finishedGoingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
         }
 
-        mWindowManagerInternal.registerAppTransitionListener(
-                mStatusBarController.getAppTransitionListener());
         mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
             @Override
             public int onAppTransitionStartingLocked(int transit, IBinder openToken,
@@ -2255,16 +1916,6 @@
         if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
             mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
         }
-
-        mNavBarOpacityMode = res.getInteger(
-                com.android.internal.R.integer.config_navBarOpacityMode);
-    }
-
-    /**
-     * @return whether the navigation bar can be hidden, e.g. the device has a navigation bar
-     */
-    private boolean canHideNavigationBar() {
-        return mDefaultDisplayPolicy.hasNavigationBar();
     }
 
     public void updateSettings() {
@@ -2322,12 +1973,6 @@
                 mHasSoftInput = hasSoftInput;
                 updateRotation = true;
             }
-            if (mImmersiveModeConfirmation != null) {
-                mImmersiveModeConfirmation.loadSetting(mCurrentUserId);
-            }
-        }
-        synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
-            PolicyControl.reloadFromSetting(mContext);
         }
         if (updateRotation) {
             updateRotation(true);
@@ -2536,84 +2181,6 @@
         return mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW) != PERMISSION_GRANTED;
     }
 
-    @Override
-    public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
-            boolean hasStatusBarServicePermission) {
-
-        final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
-        if (mScreenDecorWindows.contains(win)) {
-            if (!isScreenDecor) {
-                // No longer has the flag set, so remove from the set.
-                mScreenDecorWindows.remove(win);
-            }
-        } else if (isScreenDecor && hasStatusBarServicePermission) {
-            mScreenDecorWindows.add(win);
-        }
-
-        switch (attrs.type) {
-            case TYPE_SYSTEM_OVERLAY:
-            case TYPE_SECURE_SYSTEM_OVERLAY:
-                // These types of windows can't receive input events.
-                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-                attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-                break;
-            case TYPE_DREAM:
-            case TYPE_WALLPAPER:
-                // Dreams and wallpapers don't have an app window token and can thus not be
-                // letterboxed. Hence always let them extend under the cutout.
-                attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-                break;
-            case TYPE_STATUS_BAR:
-
-                // If the Keyguard is in a hidden state (occluded by another window), we force to
-                // remove the wallpaper and keyguard flag so that any change in-flight after setting
-                // the keyguard as occluded wouldn't set these flags again.
-                // See {@link #processKeyguardSetHiddenResultLw}.
-                if (mKeyguardOccluded) {
-                    attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-                    attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
-                }
-                break;
-
-            case TYPE_SCREENSHOT:
-                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-                break;
-
-            case TYPE_TOAST:
-                // While apps should use the dedicated toast APIs to add such windows
-                // it possible legacy apps to add the window directly. Therefore, we
-                // make windows added directly by the app behave as a toast as much
-                // as possible in terms of timeout and animation.
-                if (attrs.hideTimeoutMilliseconds < 0
-                        || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) {
-                    attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
-                }
-                attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
-                break;
-        }
-
-        if (attrs.type != TYPE_STATUS_BAR) {
-            // The status bar is the only window allowed to exhibit keyguard behavior.
-            attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
-        }
-    }
-
-    private int getImpliedSysUiFlagsForLayout(LayoutParams attrs) {
-        int impliedFlags = 0;
-        if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
-            impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
-        }
-        final boolean forceWindowDrawsStatusBarBackground =
-                (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
-        if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
-                || forceWindowDrawsStatusBarBackground
-                        && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) {
-            impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        }
-        return impliedFlags;
-    }
-
     void readLidState() {
         mDefaultDisplayPolicy.setLidState(mWindowManagerFuncs.getLidState());
     }
@@ -2660,161 +2227,10 @@
     }
 
     @Override
-    public void onOverlayChangedLw(DisplayContentInfo displayContentInfo) {
-        onConfigurationChanged(displayContentInfo);
-    }
-
-    @Override
-    public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
-        final DisplayRotation displayRotation = displayContentInfo.getDisplayRotation();
-        // TODO(multi-display): Define policy for secondary displays.
-        if (!displayRotation.isDefaultDisplay) {
-            return;
-        }
-
-        final Context uiContext = getSystemUiContext();
-        final Resources res = uiContext.getResources();
-        final int portraitRotation = displayRotation.getPortraitRotation();
-        final int upsideDownRotation = displayRotation.getUpsideDownRotation();
-        final int landscapeRotation = displayRotation.getLandscapeRotation();
-        final int seascapeRotation = displayRotation.getSeascapeRotation();
-
-        mStatusBarHeightForRotation[portraitRotation] =
-                mStatusBarHeightForRotation[upsideDownRotation] = res.getDimensionPixelSize(
-                                com.android.internal.R.dimen.status_bar_height_portrait);
-        mStatusBarHeightForRotation[landscapeRotation] =
-                mStatusBarHeightForRotation[seascapeRotation] = res.getDimensionPixelSize(
-                        com.android.internal.R.dimen.status_bar_height_landscape);
-
-        // Height of the navigation bar when presented horizontally at bottom
-        mNavigationBarHeightForRotationDefault[portraitRotation] =
-        mNavigationBarHeightForRotationDefault[upsideDownRotation] =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
-        mNavigationBarHeightForRotationDefault[landscapeRotation] =
-        mNavigationBarHeightForRotationDefault[seascapeRotation] = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.navigation_bar_height_landscape);
-
-        // Width of the navigation bar when presented vertically along one side
-        mNavigationBarWidthForRotationDefault[portraitRotation] =
-        mNavigationBarWidthForRotationDefault[upsideDownRotation] =
-        mNavigationBarWidthForRotationDefault[landscapeRotation] =
-        mNavigationBarWidthForRotationDefault[seascapeRotation] =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
-
-        if (ALTERNATE_CAR_MODE_NAV_SIZE) {
-            // Height of the navigation bar when presented horizontally at bottom
-            mNavigationBarHeightForRotationInCarMode[portraitRotation] =
-            mNavigationBarHeightForRotationInCarMode[upsideDownRotation] =
-                    res.getDimensionPixelSize(
-                            com.android.internal.R.dimen.navigation_bar_height_car_mode);
-            mNavigationBarHeightForRotationInCarMode[landscapeRotation] =
-            mNavigationBarHeightForRotationInCarMode[seascapeRotation] = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
-
-            // Width of the navigation bar when presented vertically along one side
-            mNavigationBarWidthForRotationInCarMode[portraitRotation] =
-            mNavigationBarWidthForRotationInCarMode[upsideDownRotation] =
-            mNavigationBarWidthForRotationInCarMode[landscapeRotation] =
-            mNavigationBarWidthForRotationInCarMode[seascapeRotation] =
-                    res.getDimensionPixelSize(
-                            com.android.internal.R.dimen.navigation_bar_width_car_mode);
-        }
-
-        // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
-        mExperiments.onConfigurationChanged(uiContext);
-        // EXPERIMENT END
-    }
-
-    @VisibleForTesting
-    Context getSystemUiContext() {
-        return ActivityThread.currentActivityThread().getSystemUiContext();
-    }
-
-    @Override
     public int getMaxWallpaperLayer() {
         return getWindowLayerFromTypeLw(TYPE_STATUS_BAR);
     }
 
-    private int getNavigationBarWidth(int rotation, int uiMode) {
-        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
-            return mNavigationBarWidthForRotationInCarMode[rotation];
-        } else {
-            return mNavigationBarWidthForRotationDefault[rotation];
-        }
-    }
-
-    @Override
-    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
-            int displayId, DisplayCutout displayCutout) {
-        int width = fullWidth;
-        // TODO(multi-display): Support navigation bar on secondary displays.
-        if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) {
-            // For a basic navigation bar, when we are in landscape mode we place
-            // the navigation bar to the side.
-            if (mDefaultDisplayPolicy.navigationBarCanMove() && fullWidth > fullHeight) {
-                width -= getNavigationBarWidth(rotation, uiMode);
-            }
-        }
-        if (displayCutout != null) {
-            width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
-        }
-        return width;
-    }
-
-    private int getNavigationBarHeight(int rotation, int uiMode) {
-        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
-            return mNavigationBarHeightForRotationInCarMode[rotation];
-        } else {
-            return mNavigationBarHeightForRotationDefault[rotation];
-        }
-    }
-
-    @Override
-    public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
-            int displayId, DisplayCutout displayCutout) {
-        int height = fullHeight;
-        // TODO(multi-display): Support navigation bar on secondary displays.
-        if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) {
-            // For a basic navigation bar, when we are in portrait mode we place
-            // the navigation bar to the bottom.
-            if (!mDefaultDisplayPolicy.navigationBarCanMove() || fullWidth < fullHeight) {
-                height -= getNavigationBarHeight(rotation, uiMode);
-            }
-        }
-        if (displayCutout != null) {
-            height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();
-        }
-        return height;
-    }
-
-    @Override
-    public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
-            int displayId, DisplayCutout displayCutout) {
-        return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayId,
-                displayCutout);
-    }
-
-    @Override
-    public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
-            int displayId, DisplayCutout displayCutout) {
-        // There is a separate status bar at the top of the display.  We don't count that as part
-        // of the fixed decor, since it can hide; however, for purposes of configurations,
-        // we do want to exclude it since applications can't generally use that part
-        // of the screen.
-        // TODO(multi-display): Support status bars on secondary displays.
-        if (displayId == DEFAULT_DISPLAY) {
-            int statusBarHeight = mStatusBarHeightForRotation[rotation];
-            if (displayCutout != null) {
-                // If there is a cutout, it may already have accounted for some part of the status
-                // bar height.
-                statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
-            }
-            return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayId,
-                    displayCutout) - statusBarHeight;
-        }
-        return fullHeight;
-    }
-
     @Override
     public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
         return attrs.type == TYPE_STATUS_BAR;
@@ -3054,251 +2470,6 @@
         return context.createDisplayContext(targetDisplay);
     }
 
-    /**
-     * Preflight adding a window to the system.
-     *
-     * Currently enforces that three window types are singletons:
-     * <ul>
-     * <li>STATUS_BAR_TYPE</li>
-     * <li>KEYGUARD_TYPE</li>
-     * </ul>
-     *
-     * @param win The window to be added
-     * @param attrs Information about the window to be added
-     *
-     * @return If ok, WindowManagerImpl.ADD_OKAY.  If too many singletons,
-     * WindowManagerImpl.ADD_MULTIPLE_SINGLETON
-     */
-    @Override
-    public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) {
-
-        if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.STATUS_BAR_SERVICE,
-                    "PhoneWindowManager");
-            mScreenDecorWindows.add(win);
-        }
-
-        switch (attrs.type) {
-            case TYPE_STATUS_BAR:
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE,
-                        "PhoneWindowManager");
-                if (mStatusBar != null) {
-                    if (mStatusBar.isAlive()) {
-                        return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
-                    }
-                }
-                mStatusBar = win;
-                mStatusBarController.setWindow(win);
-                setKeyguardOccludedLw(mKeyguardOccluded, true /* force */);
-                break;
-            case TYPE_NAVIGATION_BAR:
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE,
-                        "PhoneWindowManager");
-                if (mNavigationBar != null) {
-                    if (mNavigationBar.isAlive()) {
-                        return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
-                    }
-                }
-                mNavigationBar = win;
-                mNavigationBarController.setWindow(win);
-                mNavigationBarController.setOnBarVisibilityChangedListener(
-                        mNavBarVisibilityListener, true);
-                if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
-                break;
-            case TYPE_NAVIGATION_BAR_PANEL:
-            case TYPE_STATUS_BAR_PANEL:
-            case TYPE_STATUS_BAR_SUB_PANEL:
-            case TYPE_VOICE_INTERACTION_STARTING:
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE,
-                        "PhoneWindowManager");
-                break;
-        }
-        return ADD_OKAY;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void removeWindowLw(WindowState win) {
-        if (mStatusBar == win) {
-            mStatusBar = null;
-            mStatusBarController.setWindow(null);
-        } else if (mNavigationBar == win) {
-            mNavigationBar = null;
-            mNavigationBarController.setWindow(null);
-        }
-        if (mLastFocusedWindow == win) {
-            mLastFocusedWindow = null;
-        }
-        mScreenDecorWindows.remove(win);
-    }
-
-    static final boolean PRINT_ANIM = false;
-
-    /** {@inheritDoc} */
-    @Override
-    public int selectAnimationLw(WindowState win, int transit) {
-        if (PRINT_ANIM) Log.i(TAG, "selectAnimation in " + win
-              + ": transit=" + transit);
-        if (win == mStatusBar) {
-            final boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
-            final boolean expanded = win.getAttrs().height == MATCH_PARENT
-                    && win.getAttrs().width == MATCH_PARENT;
-            if (isKeyguard || expanded) {
-                return -1;
-            }
-            if (transit == TRANSIT_EXIT
-                    || transit == TRANSIT_HIDE) {
-                return R.anim.dock_top_exit;
-            } else if (transit == TRANSIT_ENTER
-                    || transit == TRANSIT_SHOW) {
-                return R.anim.dock_top_enter;
-            }
-        } else if (win == mNavigationBar) {
-            if (win.getAttrs().windowAnimations != 0) {
-                return 0;
-            }
-            // This can be on either the bottom or the right or the left.
-            if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
-                if (transit == TRANSIT_EXIT
-                        || transit == TRANSIT_HIDE) {
-                    if (isKeyguardShowingAndNotOccluded()) {
-                        return R.anim.dock_bottom_exit_keyguard;
-                    } else {
-                        return R.anim.dock_bottom_exit;
-                    }
-                } else if (transit == TRANSIT_ENTER
-                        || transit == TRANSIT_SHOW) {
-                    return R.anim.dock_bottom_enter;
-                }
-            } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
-                if (transit == TRANSIT_EXIT
-                        || transit == TRANSIT_HIDE) {
-                    return R.anim.dock_right_exit;
-                } else if (transit == TRANSIT_ENTER
-                        || transit == TRANSIT_SHOW) {
-                    return R.anim.dock_right_enter;
-                }
-            } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
-                if (transit == TRANSIT_EXIT
-                        || transit == TRANSIT_HIDE) {
-                    return R.anim.dock_left_exit;
-                } else if (transit == TRANSIT_ENTER
-                        || transit == TRANSIT_SHOW) {
-                    return R.anim.dock_left_enter;
-                }
-            }
-        } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
-            return selectDockedDividerAnimationLw(win, transit);
-        }
-
-        if (transit == TRANSIT_PREVIEW_DONE) {
-            if (win.hasAppShownWindows()) {
-                if (PRINT_ANIM) Log.i(TAG, "**** STARTING EXIT");
-                return com.android.internal.R.anim.app_starting_exit;
-            }
-        } else if (win.getAttrs().type == TYPE_DREAM && mDreamingLockscreen
-                && transit == TRANSIT_ENTER) {
-            // Special case: we are animating in a dream, while the keyguard
-            // is shown.  We don't want an animation on the dream, because
-            // we need it shown immediately with the keyguard animating away
-            // to reveal it.
-            return -1;
-        }
-
-        return 0;
-    }
-
-    private int selectDockedDividerAnimationLw(WindowState win, int transit) {
-        int insets = mWindowManagerFuncs.getDockedDividerInsetsLw();
-
-        // If the divider is behind the navigation bar, don't animate.
-        final Rect frame = win.getFrameLw();
-        final boolean behindNavBar = mNavigationBar != null
-                && ((mNavigationBarPosition == NAV_BAR_BOTTOM
-                        && frame.top + insets >= mNavigationBar.getFrameLw().top)
-                || (mNavigationBarPosition == NAV_BAR_RIGHT
-                        && frame.left + insets >= mNavigationBar.getFrameLw().left)
-                || (mNavigationBarPosition == NAV_BAR_LEFT
-                        && frame.right - insets <= mNavigationBar.getFrameLw().right));
-        final boolean landscape = frame.height() > frame.width();
-        final boolean offscreenLandscape = landscape && (frame.right - insets <= 0
-                || frame.left + insets >= win.getDisplayFrameLw().right);
-        final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0
-                || frame.bottom + insets >= win.getDisplayFrameLw().bottom);
-        final boolean offscreen = offscreenLandscape || offscreenPortrait;
-        if (behindNavBar || offscreen) {
-            return 0;
-        }
-        if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
-            return R.anim.fade_in;
-        } else if (transit == TRANSIT_EXIT) {
-            return R.anim.fade_out;
-        } else {
-            return 0;
-        }
-    }
-
-    @Override
-    public void selectRotationAnimationLw(int anim[]) {
-        // If the screen is off or non-interactive, force a jumpcut.
-        final boolean forceJumpcut = !mDefaultDisplayPolicy.isScreenOnFully() || !okToAnimate();
-        if (PRINT_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen="
-                + mTopFullscreenOpaqueWindowState + " rotationAnimation="
-                + (mTopFullscreenOpaqueWindowState == null ?
-                        "0" : mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation)
-                + " forceJumpcut=" + forceJumpcut);
-        if (forceJumpcut) {
-            anim[0] = R.anim.rotation_animation_jump_exit;
-            anim[1] = R.anim.rotation_animation_enter;
-            return;
-        }
-        if (mTopFullscreenOpaqueWindowState != null) {
-            int animationHint = mTopFullscreenOpaqueWindowState.getRotationAnimationHint();
-            if (animationHint < 0 && mTopIsFullscreen) {
-                animationHint = mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation;
-            }
-            switch (animationHint) {
-                case ROTATION_ANIMATION_CROSSFADE:
-                case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless.
-                    anim[0] = R.anim.rotation_animation_xfade_exit;
-                    anim[1] = R.anim.rotation_animation_enter;
-                    break;
-                case ROTATION_ANIMATION_JUMPCUT:
-                    anim[0] = R.anim.rotation_animation_jump_exit;
-                    anim[1] = R.anim.rotation_animation_enter;
-                    break;
-                case ROTATION_ANIMATION_ROTATE:
-                default:
-                    anim[0] = anim[1] = 0;
-                    break;
-            }
-        } else {
-            anim[0] = anim[1] = 0;
-        }
-    }
-
-    @Override
-    public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId,
-            boolean forceDefault) {
-        switch (exitAnimId) {
-            case R.anim.rotation_animation_xfade_exit:
-            case R.anim.rotation_animation_jump_exit:
-                // These are the only cases that matter.
-                if (forceDefault) {
-                    return false;
-                }
-                int anim[] = new int[2];
-                selectRotationAnimationLw(anim);
-                return (exitAnimId == anim[0] && enterAnimId == anim[1]);
-            default:
-                return true;
-        }
-    }
-
     @Override
     public Animation createHiddenByKeyguardExit(boolean onWallpaper,
             boolean goingToNotificationShade) {
@@ -4173,77 +3344,6 @@
         }
     }
 
-    private final Runnable mClearHideNavigationFlag = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
-                // Clear flags.
-                mForceClearedSystemUiFlags &=
-                        ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
-            }
-            mWindowManagerFuncs.reevaluateStatusBarVisibility();
-        }
-    };
-
-    /**
-     * Input handler used while nav bar is hidden.  Captures any touch on the screen,
-     * to determine when the nav bar should be shown and prevent applications from
-     * receiving those touches.
-     */
-    final class HideNavInputEventReceiver extends InputEventReceiver {
-        public HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) {
-            super(inputChannel, looper);
-        }
-
-        @Override
-        public void onInputEvent(InputEvent event) {
-            boolean handled = false;
-            try {
-                if (event instanceof MotionEvent
-                        && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-                    final MotionEvent motionEvent = (MotionEvent)event;
-                    if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
-                        // When the user taps down, we re-show the nav bar.
-                        boolean changed = false;
-                        synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
-                            if (mInputConsumer == null) {
-                                return;
-                            }
-                            // Any user activity always causes us to show the
-                            // navigation controls, if they had been hidden.
-                            // We also clear the low profile and only content
-                            // flags so that tapping on the screen will atomically
-                            // restore all currently hidden screen decorations.
-                            int newVal = mResettingSystemUiFlags |
-                                    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
-                                    View.SYSTEM_UI_FLAG_LOW_PROFILE |
-                                    View.SYSTEM_UI_FLAG_FULLSCREEN;
-                            if (mResettingSystemUiFlags != newVal) {
-                                mResettingSystemUiFlags = newVal;
-                                changed = true;
-                            }
-                            // We don't allow the system's nav bar to be hidden
-                            // again for 1 second, to prevent applications from
-                            // spamming us and keeping it from being shown.
-                            newVal = mForceClearedSystemUiFlags |
-                                    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
-                            if (mForceClearedSystemUiFlags != newVal) {
-                                mForceClearedSystemUiFlags = newVal;
-                                changed = true;
-                                mHandler.postDelayed(mClearHideNavigationFlag, 1000);
-                            }
-                        }
-                        if (changed) {
-                            mWindowManagerFuncs.reevaluateStatusBarVisibility();
-                        }
-                    }
-                }
-            } finally {
-                finishInputEvent(event, handled);
-            }
-        }
-    }
-
     @Override
     public void setRecentsVisibilityLw(boolean visible) {
         mRecentsVisible = visible;
@@ -4259,1177 +3359,9 @@
         mNavBarVirtualKeyHapticFeedbackEnabled = enabled;
     }
 
-    @Override
-    public int adjustSystemUiVisibilityLw(int visibility) {
-        mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
-        mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
-
-        // Reset any bits in mForceClearingStatusBarVisibility that
-        // are now clear.
-        mResettingSystemUiFlags &= visibility;
-        // Clear any bits in the new visibility that are currently being
-        // force cleared, before reporting it.
-        return visibility & ~mResettingSystemUiFlags
-                & ~mForceClearedSystemUiFlags;
-    }
-
-    @Override
-    // TODO: Should probably be moved into DisplayFrames.
-    public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds,
-            DisplayFrames displayFrames, boolean floatingStack, Rect outFrame,
-            Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
-        final int fl = PolicyControl.getWindowFlags(null, attrs);
-        final int pfl = attrs.privateFlags;
-        final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
-        final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs);
-        final int displayRotation = displayFrames.mRotation;
-
-        final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
-        if (useOutsets) {
-            int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
-            if (outset > 0) {
-                if (displayRotation == Surface.ROTATION_0) {
-                    outOutsets.bottom += outset;
-                } else if (displayRotation == Surface.ROTATION_90) {
-                    outOutsets.right += outset;
-                } else if (displayRotation == Surface.ROTATION_180) {
-                    outOutsets.top += outset;
-                } else if (displayRotation == Surface.ROTATION_270) {
-                    outOutsets.left += outset;
-                }
-            }
-        }
-
-        final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0;
-        final boolean layoutInScreenAndInsetDecor = layoutInScreen &&
-                (fl & FLAG_LAYOUT_INSET_DECOR) != 0;
-        final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
-
-        if (layoutInScreenAndInsetDecor && !screenDecor) {
-            if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
-                outFrame.set(displayFrames.mUnrestricted);
-            } else {
-                outFrame.set(displayFrames.mRestricted);
-            }
-
-            final Rect sf;
-            if (floatingStack) {
-                sf = null;
-            } else {
-                sf = displayFrames.mStable;
-            }
-
-            final Rect cf;
-            if (floatingStack) {
-                cf = null;
-            } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
-                if ((fl & FLAG_FULLSCREEN) != 0) {
-                    cf = displayFrames.mStableFullscreen;
-                } else {
-                    cf = displayFrames.mStable;
-                }
-            } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
-                cf = displayFrames.mOverscan;
-            } else {
-                cf = displayFrames.mCurrent;
-            }
-
-            if (taskBounds != null) {
-                outFrame.intersect(taskBounds);
-            }
-            InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets);
-            InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets);
-            outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame)
-                    .getDisplayCutout());
-            return mForceShowSystemBars;
-        } else {
-            if (layoutInScreen) {
-                outFrame.set(displayFrames.mUnrestricted);
-            } else {
-                outFrame.set(displayFrames.mStable);
-            }
-            if (taskBounds != null) {
-                outFrame.intersect(taskBounds);
-            }
-
-            outContentInsets.setEmpty();
-            outStableInsets.setEmpty();
-            outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
-            return mForceShowSystemBars;
-        }
-    }
-
-    private boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) {
-        return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN
-                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0;
-    }
-
     /** {@inheritDoc} */
     @Override
-    public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
-        displayFrames.onBeginLayout();
-        // TODO(multi-display): This doesn't seem right...Maybe only apply to default display?
-        mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
-        mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
-        mDockLayer = 0x10000000;
-        mStatusBarLayer = -1;
-
-        if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
-            // For purposes of putting out fake window up to steal focus, we will
-            // drive nav being hidden only by whether it is requested.
-            final int sysui = mLastSystemUiFlags;
-            boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
-            boolean navTranslucent = (sysui
-                    & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
-            boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
-            boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
-            boolean navAllowedHidden = immersive || immersiveSticky;
-            navTranslucent &= !immersiveSticky;  // transient trumps translucent
-            boolean isKeyguardShowing = isStatusBarKeyguard() && !mKeyguardOccluded;
-            if (!isKeyguardShowing) {
-                navTranslucent &= areTranslucentBarsAllowed();
-            }
-            boolean statusBarForcesShowingNavigation = !isKeyguardShowing && mStatusBar != null
-                    && (mStatusBar.getAttrs().privateFlags
-                            & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
-
-            // When the navigation bar isn't visible, we put up a fake input window to catch all
-            // touch events. This way we can detect when the user presses anywhere to bring back the
-            // nav bar and ensure the application doesn't see the event.
-            if (navVisible || navAllowedHidden) {
-                if (mInputConsumer != null) {
-                    mHandler.sendMessage(
-                            mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
-                    mInputConsumer = null;
-                }
-            } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
-                mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(),
-                        INPUT_CONSUMER_NAVIGATION,
-                        (channel, looper) -> new HideNavInputEventReceiver(channel, looper),
-                        displayFrames.mDisplayId);
-                // As long as mInputConsumer is active, hover events are not dispatched to the app
-                // and the pointer icon is likely to become stale. Hide it to avoid confusion.
-                InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
-            }
-
-            // For purposes of positioning and showing the nav bar, if we have decided that it can't
-            // be hidden (because of the screen aspect ratio), then take that into account.
-            navVisible |= !canHideNavigationBar();
-
-            boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
-                    navTranslucent, navAllowedHidden, statusBarForcesShowingNavigation);
-            if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
-            updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing);
-            if (updateSysUiVisibility) {
-                updateSystemUiVisibilityLw();
-            }
-        }
-        layoutScreenDecorWindows(displayFrames);
-
-        if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
-            // Make sure that the zone we're avoiding for the cutout is at least as tall as the
-            // status bar; otherwise fullscreen apps will end up cutting halfway into the status
-            // bar.
-            displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,
-                    displayFrames.mStable.top);
-        }
-    }
-
-    private void layoutScreenDecorWindows(DisplayFrames displayFrames) {
-        if (mScreenDecorWindows.isEmpty()) {
-            return;
-        }
-
-        sTmpRect.setEmpty();
-        sTmpDockedFrame.set(displayFrames.mDock);
-
-        final int displayId = displayFrames.mDisplayId;
-        final Rect dockFrame = displayFrames.mDock;
-        final int displayHeight = displayFrames.mDisplayHeight;
-        final int displayWidth = displayFrames.mDisplayWidth;
-
-        for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) {
-            final WindowState w = mScreenDecorWindows.valueAt(i);
-            if (w.getDisplayId() != displayId || !w.isVisibleLw()) {
-                // Skip if not on the same display or not visible.
-                continue;
-            }
-
-            w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */,
-                    sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */,
-                    sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */,
-                    sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */,
-                    sTmpDockedFrame /* outsetFrame */);
-            w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
-            w.computeFrameLw();
-            final Rect frame = w.getFrameLw();
-
-            if (frame.left <= 0 && frame.top <= 0) {
-                // Docked at left or top.
-                if (frame.bottom >= displayHeight) {
-                    // Docked left.
-                    dockFrame.left = Math.max(frame.right, dockFrame.left);
-                } else if (frame.right >= displayWidth ) {
-                    // Docked top.
-                    dockFrame.top = Math.max(frame.bottom, dockFrame.top);
-                } else {
-                    Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
-                            + " not docked on left or top of display. frame=" + frame
-                            + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
-                }
-            } else if (frame.right >= displayWidth && frame.bottom >= displayHeight) {
-                // Docked at right or bottom.
-                if (frame.top <= 0) {
-                    // Docked right.
-                    dockFrame.right = Math.min(frame.left, dockFrame.right);
-                } else if (frame.left <= 0) {
-                    // Docked bottom.
-                    dockFrame.bottom = Math.min(frame.top, dockFrame.bottom);
-                } else {
-                    Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
-                            + " not docked on right or bottom" + " of display. frame=" + frame
-                            + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
-                }
-            } else {
-                // Screen decor windows are required to be docked on one of the sides of the screen.
-                Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
-                        + " not docked on one of the sides of the display. frame=" + frame
-                        + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
-            }
-        }
-
-        displayFrames.mRestricted.set(dockFrame);
-        displayFrames.mCurrent.set(dockFrame);
-        displayFrames.mVoiceContent.set(dockFrame);
-        displayFrames.mSystem.set(dockFrame);
-        displayFrames.mContent.set(dockFrame);
-        displayFrames.mRestrictedOverscan.set(dockFrame);
-    }
-
-    private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
-            boolean isKeyguardShowing) {
-        // decide where the status bar goes ahead of time
-        if (mStatusBar == null) {
-            return false;
-        }
-        // apply any navigation bar insets
-        sTmpRect.setEmpty();
-        mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
-                displayFrames.mUnrestricted /* displayFrame */,
-                displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
-                displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
-                displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
-        mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
-        mStatusBarLayer = mStatusBar.getSurfaceLayer();
-
-        // Let the status bar determine its size.
-        mStatusBar.computeFrameLw();
-
-        // For layout, the status bar is always at the top with our fixed height.
-        displayFrames.mStable.top = displayFrames.mUnrestricted.top
-                + mStatusBarHeightForRotation[displayFrames.mRotation];
-        // Make sure the status bar covers the entire cutout height
-        displayFrames.mStable.top = Math.max(displayFrames.mStable.top,
-                displayFrames.mDisplayCutoutSafe.top);
-
-        // Tell the bar controller where the collapsed status bar content is
-        sTmpRect.set(mStatusBar.getContentFrameLw());
-        sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
-        sTmpRect.top = mStatusBar.getContentFrameLw().top;  // Ignore top display cutout inset
-        sTmpRect.bottom = displayFrames.mStable.top;  // Use collapsed status bar size
-        mStatusBarController.setContentFrame(sTmpRect);
-
-        boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
-        boolean statusBarTranslucent = (sysui
-                & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
-        if (!isKeyguardShowing) {
-            statusBarTranslucent &= areTranslucentBarsAllowed();
-        }
-
-        // If the status bar is hidden, we don't want to cause windows behind it to scroll.
-        if (mStatusBar.isVisibleLw() && !statusBarTransient) {
-            // Status bar may go away, so the screen area it occupies is available to apps but just
-            // covering them when the status bar is visible.
-            final Rect dockFrame = displayFrames.mDock;
-            dockFrame.top = displayFrames.mStable.top;
-            displayFrames.mContent.set(dockFrame);
-            displayFrames.mVoiceContent.set(dockFrame);
-            displayFrames.mCurrent.set(dockFrame);
-
-            if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
-                    "dock=%s content=%s cur=%s", dockFrame.toString(),
-                    displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
-
-            if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
-                    && !mStatusBarController.wasRecentlyTranslucent()) {
-                // If the opaque status bar is currently requested to be visible, and not in the
-                // process of animating on or off, then we can tell the app that it is covered by it.
-                displayFrames.mSystem.top = displayFrames.mStable.top;
-            }
-        }
-        return mStatusBarController.checkHiddenLw();
-    }
-
-    private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
-            boolean navTranslucent, boolean navAllowedHidden,
-            boolean statusBarForcesShowingNavigation) {
-        if (mNavigationBar == null) {
-            return false;
-        }
-
-        final Rect navigationFrame = sTmpNavFrame;
-        boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
-        // Force the navigation bar to its appropriate place and size. We need to do this directly,
-        // instead of relying on it to bubble up from the nav bar, because this needs to change
-        // atomically with screen rotations.
-        final int rotation = displayFrames.mRotation;
-        final int displayHeight = displayFrames.mDisplayHeight;
-        final int displayWidth = displayFrames.mDisplayWidth;
-        final Rect dockFrame = displayFrames.mDock;
-        mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
-
-        final Rect cutoutSafeUnrestricted = sTmpRect;
-        cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
-        cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
-
-        if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
-            // It's a system nav bar or a portrait screen; nav bar goes on bottom.
-            final int top = cutoutSafeUnrestricted.bottom
-                    - getNavigationBarHeight(rotation, uiMode);
-            // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
-            final int topNavBar = cutoutSafeUnrestricted.bottom
-                    - mExperiments.getNavigationBarFrameHeight();
-            navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom);
-            // EXPERIMENT END
-            displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
-            if (transientNavBarShowing) {
-                mNavigationBarController.setBarShowingLw(true);
-            } else if (navVisible) {
-                mNavigationBarController.setBarShowingLw(true);
-                dockFrame.bottom = displayFrames.mRestricted.bottom
-                        = displayFrames.mRestrictedOverscan.bottom = top;
-            } else {
-                // We currently want to hide the navigation UI - unless we expanded the status bar.
-                mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
-            }
-            if (navVisible && !navTranslucent && !navAllowedHidden
-                    && !mNavigationBar.isAnimatingLw()
-                    && !mNavigationBarController.wasRecentlyTranslucent()) {
-                // If the opaque nav bar is currently requested to be visible and not in the process
-                // of animating on or off, then we can tell the app that it is covered by it.
-                displayFrames.mSystem.bottom = top;
-            }
-        } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
-            // Landscape screen; nav bar goes to the right.
-            final int left = cutoutSafeUnrestricted.right
-                    - getNavigationBarWidth(rotation, uiMode);
-            // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
-            final int leftNavBar = cutoutSafeUnrestricted.right
-                    - mExperiments.getNavigationBarFrameWidth();
-            navigationFrame.set(leftNavBar, 0, displayFrames.mUnrestricted.right, displayHeight);
-            // EXPERIMENT END
-            displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
-            if (transientNavBarShowing) {
-                mNavigationBarController.setBarShowingLw(true);
-            } else if (navVisible) {
-                mNavigationBarController.setBarShowingLw(true);
-                dockFrame.right = displayFrames.mRestricted.right
-                        = displayFrames.mRestrictedOverscan.right = left;
-            } else {
-                // We currently want to hide the navigation UI - unless we expanded the status bar.
-                mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
-            }
-            if (navVisible && !navTranslucent && !navAllowedHidden
-                    && !mNavigationBar.isAnimatingLw()
-                    && !mNavigationBarController.wasRecentlyTranslucent()) {
-                // If the nav bar is currently requested to be visible, and not in the process of
-                // animating on or off, then we can tell the app that it is covered by it.
-                displayFrames.mSystem.right = left;
-            }
-        } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
-            // Seascape screen; nav bar goes to the left.
-            final int right = cutoutSafeUnrestricted.left
-                    + getNavigationBarWidth(rotation, uiMode);
-            // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
-            final int rightNavBar = cutoutSafeUnrestricted.left
-                    + mExperiments.getNavigationBarFrameWidth();
-            navigationFrame.set(displayFrames.mUnrestricted.left, 0, rightNavBar, displayHeight);
-            // EXPERIMENT END
-            displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
-            if (transientNavBarShowing) {
-                mNavigationBarController.setBarShowingLw(true);
-            } else if (navVisible) {
-                mNavigationBarController.setBarShowingLw(true);
-                dockFrame.left = displayFrames.mRestricted.left =
-                        displayFrames.mRestrictedOverscan.left = right;
-            } else {
-                // We currently want to hide the navigation UI - unless we expanded the status bar.
-                mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
-            }
-            if (navVisible && !navTranslucent && !navAllowedHidden
-                    && !mNavigationBar.isAnimatingLw()
-                    && !mNavigationBarController.wasRecentlyTranslucent()) {
-                // If the nav bar is currently requested to be visible, and not in the process of
-                // animating on or off, then we can tell the app that it is covered by it.
-                displayFrames.mSystem.left = right;
-            }
-        }
-
-        // Make sure the content and current rectangles are updated to account for the restrictions
-        // from the navigation bar.
-        displayFrames.mCurrent.set(dockFrame);
-        displayFrames.mVoiceContent.set(dockFrame);
-        displayFrames.mContent.set(dockFrame);
-        mStatusBarLayer = mNavigationBar.getSurfaceLayer();
-        // And compute the final frame.
-        sTmpRect.setEmpty();
-        mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */,
-                navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
-                displayFrames.mDisplayCutoutSafe /* contentFrame */,
-                navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
-                navigationFrame /* stableFrame */,
-                displayFrames.mDisplayCutoutSafe /* outsetFrame */);
-        mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
-        mNavigationBar.computeFrameLw();
-        mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
-
-        if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
-        return mNavigationBarController.checkHiddenLw();
-    }
-
-    @NavigationBarPosition
-    private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
-        if (mDefaultDisplayPolicy.navigationBarCanMove() && displayWidth > displayHeight) {
-            if (displayRotation == Surface.ROTATION_270) {
-                return NAV_BAR_LEFT;
-            } else {
-                return NAV_BAR_RIGHT;
-            }
-        }
-        return NAV_BAR_BOTTOM;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public int getSystemDecorLayerLw() {
-        if (mStatusBar != null && mStatusBar.isVisibleLw()) {
-            return mStatusBar.getSurfaceLayer();
-        }
-
-        if (mNavigationBar != null && mNavigationBar.isVisibleLw()) {
-            return mNavigationBar.getSurfaceLayer();
-        }
-
-        return 0;
-    }
-
-    private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
-            boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf,
-            DisplayFrames displayFrames) {
-        if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) {
-            // Here's a special case: if the child window is not the 'dock window'
-            // or input method target, and the window it is attached to is below
-            // the dock window, then the frames we computed for the window it is
-            // attached to can not be used because the dock is effectively part
-            // of the underlying window and the attached window is floating on top
-            // of the whole thing. So, we ignore the attached window and explicitly
-            // compute the frames that would be appropriate without the dock.
-            vf.set(displayFrames.mDock);
-            cf.set(displayFrames.mDock);
-            of.set(displayFrames.mDock);
-            df.set(displayFrames.mDock);
-        } else {
-            // The effective display frame of the attached window depends on whether it is taking
-            // care of insetting its content. If not, we need to use the parent's content frame so
-            // that the entire window is positioned within that content. Otherwise we can use the
-            // overscan frame and let the attached window take care of positioning its content
-            // appropriately.
-            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                // Set the content frame of the attached window to the parent's decor frame
-                // (same as content frame when IME isn't present) if specifically requested by
-                // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag.
-                // Otherwise, use the overscan frame.
-                cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0
-                        ? attached.getContentFrameLw() : attached.getOverscanFrameLw());
-            } else {
-                // If the window is resizing, then we want to base the content frame on our attached
-                // content frame to resize...however, things can be tricky if the attached window is
-                // NOT in resize mode, in which case its content frame will be larger.
-                // Ungh. So to deal with that, make sure the content frame we end up using is not
-                // covering the IM dock.
-                cf.set(attached.getContentFrameLw());
-                if (attached.isVoiceInteraction()) {
-                    cf.intersectUnchecked(displayFrames.mVoiceContent);
-                } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) {
-                    cf.intersectUnchecked(displayFrames.mContent);
-                }
-            }
-            df.set(insetDecors ? attached.getDisplayFrameLw() : cf);
-            of.set(insetDecors ? attached.getOverscanFrameLw() : cf);
-            vf.set(attached.getVisibleFrameLw());
-        }
-        // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be
-        // positioned relative to its parent or the entire screen.
-        pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df);
-    }
-
-    private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) {
-        if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) {
-            return;
-        }
-        // If app is requesting a stable layout, don't let the content insets go below the stable
-        // values.
-        if ((fl & FLAG_FULLSCREEN) != 0) {
-            r.intersectUnchecked(displayFrames.mStableFullscreen);
-        } else {
-            r.intersectUnchecked(displayFrames.mStable);
-        }
-    }
-
-    private boolean canReceiveInput(WindowState win) {
-        boolean notFocusable =
-                (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0;
-        boolean altFocusableIm =
-                (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0;
-        boolean notFocusableForIm = notFocusable ^ altFocusableIm;
-        return !notFocusableForIm;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
-        // We've already done the navigation bar, status bar, and all screen decor windows. If the
-        // status bar can receive input, we need to layout it again to accommodate for the IME
-        // window.
-        if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar
-                || mScreenDecorWindows.contains(win)) {
-            return;
-        }
-        final WindowManager.LayoutParams attrs = win.getAttrs();
-        final boolean isDefaultDisplay = win.isDefaultDisplay();
-
-        final int type = attrs.type;
-        final int fl = PolicyControl.getWindowFlags(win, attrs);
-        final int pfl = attrs.privateFlags;
-        final int sim = attrs.softInputMode;
-        final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
-        final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
-
-        final WindowFrames windowFrames = win.getWindowFrames();
-
-        windowFrames.setHasOutsets(false);
-        sTmpLastParentFrame.set(windowFrames.mParentFrame);
-        final Rect pf = windowFrames.mParentFrame;
-        final Rect df = windowFrames.mDisplayFrame;
-        final Rect of = windowFrames.mOverscanFrame;
-        final Rect cf = windowFrames.mContentFrame;
-        final Rect vf = windowFrames.mVisibleFrame;
-        final Rect dcf = windowFrames.mDecorFrame;
-        final Rect sf = windowFrames.mStableFrame;
-        dcf.setEmpty();
-        windowFrames.setParentFrameWasClippedByDisplayCutout(false);
-        windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
-
-        final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar()
-                && mNavigationBar != null && mNavigationBar.isVisibleLw());
-
-        final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
-
-        final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
-                || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
-
-        final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
-        final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
-
-        sf.set(displayFrames.mStable);
-
-        if (type == TYPE_INPUT_METHOD) {
-            vf.set(displayFrames.mDock);
-            cf.set(displayFrames.mDock);
-            of.set(displayFrames.mDock);
-            df.set(displayFrames.mDock);
-            windowFrames.mParentFrame.set(displayFrames.mDock);
-            // IM dock windows layout below the nav bar...
-            pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
-            // ...with content insets above the nav bar
-            cf.bottom = vf.bottom = displayFrames.mStable.bottom;
-            // TODO (b/111364446): Support showing IME on non-default displays
-            if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
-                // The status bar forces the navigation bar while it's visible. Make sure the IME
-                // avoids the navigation bar in that case.
-                if (mNavigationBarPosition == NAV_BAR_RIGHT) {
-                    pf.right = df.right = of.right = cf.right = vf.right =
-                            displayFrames.mStable.right;
-                } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
-                    pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
-                }
-            }
-
-            // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
-            // Offset the ime to avoid overlapping with the nav bar
-            mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win);
-            // EXPERIMENT END
-
-            // IM dock windows always go to the bottom of the screen.
-            attrs.gravity = Gravity.BOTTOM;
-            mDockLayer = win.getSurfaceLayer();
-        } else if (type == TYPE_VOICE_INTERACTION) {
-            of.set(displayFrames.mUnrestricted);
-            df.set(displayFrames.mUnrestricted);
-            pf.set(displayFrames.mUnrestricted);
-            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                cf.set(displayFrames.mDock);
-            } else {
-                cf.set(displayFrames.mContent);
-            }
-            if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                vf.set(displayFrames.mCurrent);
-            } else {
-                vf.set(cf);
-            }
-        } else if (type == TYPE_WALLPAPER) {
-           layoutWallpaper(displayFrames, pf, df, of, cf);
-        } else if (win == mStatusBar) {
-            of.set(displayFrames.mUnrestricted);
-            df.set(displayFrames.mUnrestricted);
-            pf.set(displayFrames.mUnrestricted);
-            cf.set(displayFrames.mStable);
-            vf.set(displayFrames.mStable);
-
-            if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
-                cf.bottom = displayFrames.mContent.bottom;
-            } else {
-                cf.bottom = displayFrames.mDock.bottom;
-                vf.bottom = displayFrames.mContent.bottom;
-            }
-        } else {
-            dcf.set(displayFrames.mSystem);
-            final boolean inheritTranslucentDecor =
-                    (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
-            final boolean isAppWindow =
-                    type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
-            final boolean topAtRest =
-                    win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
-            if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
-                if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
-                        && (fl & FLAG_FULLSCREEN) == 0
-                        && (fl & FLAG_TRANSLUCENT_STATUS) == 0
-                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
-                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) {
-                    // Ensure policy decor includes status bar
-                    dcf.top = displayFrames.mStable.top;
-                }
-                if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
-                        && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
-                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
-                    // Ensure policy decor includes navigation bar
-                    dcf.bottom = displayFrames.mStable.bottom;
-                    dcf.right = displayFrames.mStable.right;
-                }
-            }
-
-            if (layoutInScreen && layoutInsetDecor) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
-                            + "): IN_SCREEN, INSET_DECOR");
-                // This is the case for a normal activity window: we want it to cover all of the
-                // screen space, and it can take care of moving its contents to account for screen
-                // decorations that intrude into that space.
-                if (attached != null) {
-                    // If this window is attached to another, our display
-                    // frame is the same as the one we are attached to.
-                    setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf,
-                            displayFrames);
-                } else {
-                    if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
-                        // Status bar panels are the only windows who can go on top of the status
-                        // bar. They are protected by the STATUS_BAR_SERVICE permission, so they
-                        // have the same privileges as the status bar itself.
-                        //
-                        // However, they should still dodge the navigation bar if it exists.
-
-                        pf.left = df.left = of.left = hasNavBar
-                                ? displayFrames.mDock.left : displayFrames.mUnrestricted.left;
-                        pf.top = df.top = of.top = displayFrames.mUnrestricted.top;
-                        pf.right = df.right = of.right = hasNavBar
-                                ? displayFrames.mRestricted.right
-                                : displayFrames.mUnrestricted.right;
-                        pf.bottom = df.bottom = of.bottom = hasNavBar
-                                ? displayFrames.mRestricted.bottom
-                                : displayFrames.mUnrestricted.bottom;
-
-                        if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf);
-                    } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
-                            && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
-                        // Asking to layout into the overscan region, so give it that pure
-                        // unrestricted area.
-                        of.set(displayFrames.mOverscan);
-                        df.set(displayFrames.mOverscan);
-                        pf.set(displayFrames.mOverscan);
-                    } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
-                            && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW
-                            || type == TYPE_VOLUME_OVERLAY)) {
-                        // Asking for layout as if the nav bar is hidden, lets the application
-                        // extend into the unrestricted overscan screen area. We only do this for
-                        // application windows and certain system windows to ensure no window that
-                        // can be above the nav bar can do this.
-                        df.set(displayFrames.mOverscan);
-                        pf.set(displayFrames.mOverscan);
-                        // We need to tell the app about where the frame inside the overscan is, so
-                        // it can inset its content by that amount -- it didn't ask to actually
-                        // extend itself into the overscan region.
-                        of.set(displayFrames.mUnrestricted);
-                    } else {
-                        df.set(displayFrames.mRestrictedOverscan);
-                        pf.set(displayFrames.mRestrictedOverscan);
-                        // We need to tell the app about where the frame inside the overscan
-                        // is, so it can inset its content by that amount -- it didn't ask
-                        // to actually extend itself into the overscan region.
-                        of.set(displayFrames.mUnrestricted);
-                    }
-
-                    if ((fl & FLAG_FULLSCREEN) == 0) {
-                        if (win.isVoiceInteraction()) {
-                            cf.set(displayFrames.mVoiceContent);
-                        } else {
-                            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                                cf.set(displayFrames.mDock);
-                            } else {
-                                cf.set(displayFrames.mContent);
-                            }
-                        }
-                    } else {
-                        // Full screen windows are always given a layout that is as if the status
-                        // bar and other transient decors are gone. This is to avoid bad states when
-                        // moving from a window that is not hiding the status bar to one that is.
-                        cf.set(displayFrames.mRestricted);
-                    }
-                    applyStableConstraints(sysUiFl, fl, cf, displayFrames);
-                    if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                        vf.set(displayFrames.mCurrent);
-                    } else {
-                        vf.set(cf);
-                    }
-
-                    // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
-                    mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win);
-                    // EXPERIMENT END
-                }
-            } else if (layoutInScreen || (sysUiFl
-                    & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
-                        + "): IN_SCREEN");
-                // A window that has requested to fill the entire screen just
-                // gets everything, period.
-                if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
-                    cf.set(displayFrames.mUnrestricted);
-                    of.set(displayFrames.mUnrestricted);
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
-                    if (hasNavBar) {
-                        pf.left = df.left = of.left = cf.left = displayFrames.mDock.left;
-                        pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right;
-                        pf.bottom = df.bottom = of.bottom = cf.bottom =
-                                displayFrames.mRestricted.bottom;
-                    }
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf);
-                } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
-                    // The navigation bar has Real Ultimate Power.
-                    of.set(displayFrames.mUnrestricted);
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf);
-                } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
-                        && ((fl & FLAG_FULLSCREEN) != 0)) {
-                    // Fullscreen secure system overlays get what they ask for. Screenshot region
-                    // selection overlay should also expand to full screen.
-                    cf.set(displayFrames.mOverscan);
-                    of.set(displayFrames.mOverscan);
-                    df.set(displayFrames.mOverscan);
-                    pf.set(displayFrames.mOverscan);
-                } else if (type == TYPE_BOOT_PROGRESS) {
-                    // Boot progress screen always covers entire display.
-                    cf.set(displayFrames.mOverscan);
-                    of.set(displayFrames.mOverscan);
-                    df.set(displayFrames.mOverscan);
-                    pf.set(displayFrames.mOverscan);
-                } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
-                        && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
-                    // Asking to layout into the overscan region, so give it that pure unrestricted
-                    // area.
-                    cf.set(displayFrames.mOverscan);
-                    of.set(displayFrames.mOverscan);
-                    df.set(displayFrames.mOverscan);
-                    pf.set(displayFrames.mOverscan);
-                } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
-                        && (type == TYPE_STATUS_BAR
-                            || type == TYPE_TOAST
-                            || type == TYPE_DOCK_DIVIDER
-                            || type == TYPE_VOICE_INTERACTION_STARTING
-                            || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) {
-                    // Asking for layout as if the nav bar is hidden, lets the
-                    // application extend into the unrestricted screen area.  We
-                    // only do this for application windows (or toasts) to ensure no window that
-                    // can be above the nav bar can do this.
-                    // XXX This assumes that an app asking for this will also
-                    // ask for layout in only content.  We can't currently figure out
-                    // what the screen would be if only laying out to hide the nav bar.
-                    cf.set(displayFrames.mUnrestricted);
-                    of.set(displayFrames.mUnrestricted);
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
-                } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
-                    of.set(displayFrames.mRestricted);
-                    df.set(displayFrames.mRestricted);
-                    pf.set(displayFrames.mRestricted);
-                    if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                        cf.set(displayFrames.mDock);
-                    } else {
-                        cf.set(displayFrames.mContent);
-                    }
-                } else {
-                    cf.set(displayFrames.mRestricted);
-                    of.set(displayFrames.mRestricted);
-                    df.set(displayFrames.mRestricted);
-                    pf.set(displayFrames.mRestricted);
-                }
-
-                applyStableConstraints(sysUiFl, fl, cf,displayFrames);
-
-                if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                    vf.set(displayFrames.mCurrent);
-                } else {
-                    vf.set(cf);
-                }
-            } else if (attached != null) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
-                        + "): attached to " + attached);
-                // A child window should be placed inside of the same visible
-                // frame that its parent had.
-                setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf,
-                        displayFrames);
-            } else {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
-                        "): normal window");
-                // Otherwise, a normal window must be placed inside the content
-                // of all screen decorations.
-                if (type == TYPE_STATUS_BAR_PANEL) {
-                    // Status bar panels can go on
-                    // top of the status bar. They are protected by the STATUS_BAR_SERVICE
-                    // permission, so they have the same privileges as the status bar itself.
-                    cf.set(displayFrames.mRestricted);
-                    of.set(displayFrames.mRestricted);
-                    df.set(displayFrames.mRestricted);
-                    pf.set(displayFrames.mRestricted);
-                } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
-                    // These dialogs are stable to interim decor changes.
-                    cf.set(displayFrames.mStable);
-                    of.set(displayFrames.mStable);
-                    df.set(displayFrames.mStable);
-                    pf.set(displayFrames.mStable);
-                } else {
-                    pf.set(displayFrames.mContent);
-                    if (win.isVoiceInteraction()) {
-                        cf.set(displayFrames.mVoiceContent);
-                        of.set(displayFrames.mVoiceContent);
-                        df.set(displayFrames.mVoiceContent);
-                    } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                        cf.set(displayFrames.mDock);
-                        of.set(displayFrames.mDock);
-                        df.set(displayFrames.mDock);
-                    } else {
-                        cf.set(displayFrames.mContent);
-                        of.set(displayFrames.mContent);
-                        df.set(displayFrames.mContent);
-                    }
-                    if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                        vf.set(displayFrames.mCurrent);
-                    } else {
-                        vf.set(cf);
-                    }
-                }
-            }
-        }
-
-        final int cutoutMode = attrs.layoutInDisplayCutoutMode;
-        final boolean attachedInParent = attached != null && !layoutInScreen;
-        final boolean requestedHideNavigation =
-                (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
-
-        // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get
-        // cropped / shifted to the displayFrame in WindowState.
-        final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
-                && type != TYPE_BASE_APPLICATION;
-
-        // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
-        // the cutout safe zone.
-        if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
-            final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
-            displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
-            if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
-                    && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
-                // At the top we have the status bar, so apps that are
-                // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
-                // already expect that there's an inset there and we don't need to exclude
-                // the window from that area.
-                displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
-            }
-            if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation
-                    && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
-                // Same for the navigation bar.
-                switch (mNavigationBarPosition) {
-                    case NAV_BAR_BOTTOM:
-                        displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
-                        break;
-                    case NAV_BAR_RIGHT:
-                        displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
-                        break;
-                    case NAV_BAR_LEFT:
-                        displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
-                        break;
-                }
-            }
-            if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) {
-                // The IME can always extend under the bottom cutout if the navbar is there.
-                displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
-            }
-            // Windows that are attached to a parent and laid out in said parent already avoid
-            // the cutout according to that parent and don't need to be further constrained.
-            // Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
-            // They will later be cropped or shifted using the displayFrame in WindowState,
-            // which prevents overlap with the DisplayCutout.
-            if (!attachedInParent && !floatingInScreenWindow) {
-                sTmpRect.set(pf);
-                pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
-                windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
-            }
-            // Make sure that NO_LIMITS windows clipped to the display don't extend under the
-            // cutout.
-            df.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
-        }
-
-        // Content should never appear in the cutout.
-        cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
-
-        // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
-        // Also, we don't allow windows in multi-window mode to extend out of the screen.
-        if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
-                && !win.isInMultiWindowMode()) {
-            df.left = df.top = -10000;
-            df.right = df.bottom = 10000;
-            if (type != TYPE_WALLPAPER) {
-                of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000;
-                of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
-            }
-        }
-
-        // If the device has a chin (e.g. some watches), a dead area at the bottom of the screen we
-        // need to provide information to the clients that want to pretend that you can draw there.
-        // We only want to apply outsets to certain types of windows. For example, we never want to
-        // apply the outsets to floating dialogs, because they wouldn't make sense there.
-        final boolean useOutsets = shouldUseOutsets(attrs, fl);
-        if (isDefaultDisplay && useOutsets) {
-            final Rect osf = windowFrames.mOutsetFrame;
-            osf.set(cf.left, cf.top, cf.right, cf.bottom);
-            windowFrames.setHasOutsets(true);
-            int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
-            if (outset > 0) {
-                int rotation = displayFrames.mRotation;
-                if (rotation == Surface.ROTATION_0) {
-                    osf.bottom += outset;
-                } else if (rotation == Surface.ROTATION_90) {
-                    osf.right += outset;
-                } else if (rotation == Surface.ROTATION_180) {
-                    osf.top -= outset;
-                } else if (rotation == Surface.ROTATION_270) {
-                    osf.left -= outset;
-                }
-                if (DEBUG_LAYOUT) Slog.v(TAG, "applying bottom outset of " + outset
-                        + " with rotation " + rotation + ", result: " + osf);
-            }
-        }
-
-        if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
-                + ": sim=#" + Integer.toHexString(sim)
-                + " attach=" + attached + " type=" + type
-                + String.format(" flags=0x%08x", fl)
-                + " pf=" + pf.toShortString() + " df=" + df.toShortString()
-                + " of=" + of.toShortString()
-                + " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
-                + " dcf=" + dcf.toShortString()
-                + " sf=" + sf.toShortString()
-                + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win);
-
-        if (!sTmpLastParentFrame.equals(pf)) {
-            windowFrames.setContentChanged(true);
-        }
-
-        win.computeFrameLw();
-        // Dock windows carve out the bottom of the screen, so normal windows
-        // can't appear underneath them.
-        if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
-                && !win.getGivenInsetsPendingLw()) {
-            offsetInputMethodWindowLw(win, displayFrames);
-        }
-        if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
-                && !win.getGivenInsetsPendingLw()) {
-            offsetVoiceInputWindowLw(win, displayFrames);
-        }
-    }
-
-    private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) {
-        // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area.
-        df.set(displayFrames.mOverscan);
-        pf.set(displayFrames.mOverscan);
-        cf.set(displayFrames.mUnrestricted);
-        of.set(displayFrames.mUnrestricted);
-    }
-
-    private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
-        int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
-        top += win.getGivenContentInsetsLw().top;
-        displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
-        displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
-        top = win.getVisibleFrameLw().top;
-        top += win.getGivenVisibleInsetsLw().top;
-        displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top);
-        if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom="
-                + displayFrames.mDock.bottom + " mContentBottom="
-                + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom);
-    }
-
-    private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) {
-        int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
-        top += win.getGivenContentInsetsLw().top;
-        displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
-        mTopFullscreenOpaqueWindowState = null;
-        mTopFullscreenOpaqueOrDimmingWindowState = null;
-        mTopDockedOpaqueWindowState = null;
-        mTopDockedOpaqueOrDimmingWindowState = null;
-        mForceStatusBar = false;
-        mForceStatusBarFromKeyguard = false;
-        mForceStatusBarTransparent = false;
-        mForcingShowNavBar = false;
-        mForcingShowNavBarLayer = -1;
-
-        mAllowLockscreenWhenOn = false;
-        mShowingDream = false;
-        mWindowSleepTokenNeeded = false;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
-            WindowState attached, WindowState imeTarget) {
-        final boolean affectsSystemUi = win.canAffectSystemUiFlags();
-        if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
-        applyKeyguardPolicyLw(win, imeTarget);
-        final int fl = PolicyControl.getWindowFlags(win, attrs);
-        if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi
-                && attrs.type == TYPE_INPUT_METHOD) {
-            mForcingShowNavBar = true;
-            mForcingShowNavBarLayer = win.getSurfaceLayer();
-        }
-        if (attrs.type == TYPE_STATUS_BAR) {
-            if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
-                mForceStatusBarFromKeyguard = true;
-            }
-            if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
-                mForceStatusBarTransparent = true;
-            }
-        }
-
-        boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
-                && attrs.type < FIRST_SYSTEM_WINDOW;
-        final int windowingMode = win.getWindowingMode();
-        final boolean inFullScreenOrSplitScreenSecondaryWindowingMode =
-                windowingMode == WINDOWING_MODE_FULLSCREEN
-                        || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-        if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) {
-            if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
-                mForceStatusBar = true;
-            }
-            if (attrs.type == TYPE_DREAM) {
-                // If the lockscreen was showing when the dream started then wait
-                // for the dream to draw before hiding the lockscreen.
-                if (!mDreamingLockscreen
-                        || (win.isVisibleLw() && win.hasDrawnLw())) {
-                    mShowingDream = true;
-                    appWindow = true;
-                }
-            }
-
-            // For app windows that are not attached, we decide if all windows in the app they
-            // represent should be hidden or if we should hide the lockscreen. For attached app
-            // windows we defer the decision to the window it is attached to.
-            if (appWindow && attached == null) {
-                if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
-                    mTopFullscreenOpaqueWindowState = win;
-                    if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
-                        mTopFullscreenOpaqueOrDimmingWindowState = win;
-                    }
-                    if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
-                        mAllowLockscreenWhenOn = true;
-                    }
-                }
-            }
-        }
-
-        // Voice interaction overrides both top fullscreen and top docked.
-        if (affectsSystemUi && win.getAttrs().type == TYPE_VOICE_INTERACTION) {
-            if (mTopFullscreenOpaqueWindowState == null) {
-                mTopFullscreenOpaqueWindowState = win;
-                if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
-                    mTopFullscreenOpaqueOrDimmingWindowState = win;
-                }
-            }
-            if (mTopDockedOpaqueWindowState == null) {
-                mTopDockedOpaqueWindowState = win;
-                if (mTopDockedOpaqueOrDimmingWindowState == null) {
-                    mTopDockedOpaqueOrDimmingWindowState = win;
-                }
-            }
-        }
-
-        // Keep track of the window if it's dimming but not necessarily fullscreen.
-        if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi
-                && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
-            mTopFullscreenOpaqueOrDimmingWindowState = win;
-        }
-
-        // We need to keep track of the top "fullscreen" opaque window for the docked stack
-        // separately, because both the "real fullscreen" opaque window and the one for the docked
-        // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
-        if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
-                && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            mTopDockedOpaqueWindowState = win;
-            if (mTopDockedOpaqueOrDimmingWindowState == null) {
-                mTopDockedOpaqueOrDimmingWindowState = win;
-            }
-        }
-
-        // Also keep track of any windows that are dimming but not necessarily fullscreen in the
-        // docked stack.
-        if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming()
-                && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            mTopDockedOpaqueOrDimmingWindowState = win;
-        }
-
-        // Take note if a window wants to acquire a sleep token.
-        if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0
-                && win.canAcquireSleepToken()) {
-            mWindowSleepTokenNeeded = true;
-        }
-    }
-
-    private void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) {
+    public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) {
         if (canBeHiddenByKeyguardLw(win)) {
             if (shouldBeHiddenByKeyguard(win, imeTarget)) {
                 win.hideLw(false /* doAnimation */);
@@ -5441,148 +3373,9 @@
 
     /** {@inheritDoc} */
     @Override
-    public int finishPostLayoutPolicyLw() {
-        int changes = 0;
-        boolean topIsFullscreen = false;
-
-        final WindowManager.LayoutParams lp = (mTopFullscreenOpaqueWindowState != null)
-                ? mTopFullscreenOpaqueWindowState.getAttrs()
-                : null;
-
-        // If we are not currently showing a dream then remember the current
-        // lockscreen state.  We will use this to determine whether the dream
-        // started while the lockscreen was showing and remember this state
-        // while the dream is showing.
-        if (!mShowingDream) {
-            mDreamingLockscreen = isKeyguardShowingAndNotOccluded();
-            if (mDreamingSleepTokenNeeded) {
-                mDreamingSleepTokenNeeded = false;
-                mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget();
-            }
-        } else {
-            if (!mDreamingSleepTokenNeeded) {
-                mDreamingSleepTokenNeeded = true;
-                mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget();
-            }
-        }
-
-        if (mStatusBar != null) {
-            if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar
-                    + " forcefkg=" + mForceStatusBarFromKeyguard
-                    + " top=" + mTopFullscreenOpaqueWindowState);
-            boolean shouldBeTransparent = mForceStatusBarTransparent
-                    && !mForceStatusBar
-                    && !mForceStatusBarFromKeyguard;
-            if (!shouldBeTransparent) {
-                mStatusBarController.setShowTransparent(false /* transparent */);
-            } else if (!mStatusBar.isVisibleLw()) {
-                mStatusBarController.setShowTransparent(true /* transparent */);
-            }
-
-            boolean statusBarForcesShowingNavigation
-                    = (mStatusBar.getAttrs().privateFlags
-                            & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
-            boolean topAppHidesStatusBar = topAppHidesStatusBar();
-            if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent
-                    || statusBarForcesShowingNavigation) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
-                if (mStatusBarController.setBarShowingLw(true)) {
-                    changes |= FINISH_LAYOUT_REDO_LAYOUT;
-                }
-                // Maintain fullscreen layout until incoming animation is complete.
-                topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw();
-                // Transient status bar is not allowed if status bar is on lockscreen or status bar
-                // is expecting the navigation keys from the user.
-                if ((mForceStatusBarFromKeyguard || statusBarForcesShowingNavigation)
-                        && mStatusBarController.isTransientShowing()) {
-                    mStatusBarController.updateVisibilityLw(false /*transientAllowed*/,
-                            mLastSystemUiFlags, mLastSystemUiFlags);
-                }
-            } else if (mTopFullscreenOpaqueWindowState != null) {
-                topIsFullscreen = topAppHidesStatusBar;
-                // The subtle difference between the window for mTopFullscreenOpaqueWindowState
-                // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
-                // has the FLAG_FULLSCREEN set.  Not sure if there is another way that to be the
-                // case though.
-                if (mStatusBarController.isTransientShowing()) {
-                    if (mStatusBarController.setBarShowingLw(true)) {
-                        changes |= FINISH_LAYOUT_REDO_LAYOUT;
-                    }
-                } else if (topIsFullscreen
-                        && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM)
-                        && !mWindowManagerInternal.isStackVisible(
-                                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
-                    if (mStatusBarController.setBarShowingLw(false)) {
-                        changes |= FINISH_LAYOUT_REDO_LAYOUT;
-                    } else {
-                        if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding");
-                    }
-                } else {
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen");
-                    if (mStatusBarController.setBarShowingLw(true)) {
-                        changes |= FINISH_LAYOUT_REDO_LAYOUT;
-                    }
-                    topAppHidesStatusBar = false;
-                }
-            }
-            mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar);
-        }
-
-        if (mTopIsFullscreen != topIsFullscreen) {
-            if (!topIsFullscreen) {
-                // Force another layout when status bar becomes fully shown.
-                changes |= FINISH_LAYOUT_REDO_LAYOUT;
-            }
-            mTopIsFullscreen = topIsFullscreen;
-        }
-
-        if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
-            // If the navigation bar has been hidden or shown, we need to do another
-            // layout pass to update that window.
-            changes |= FINISH_LAYOUT_REDO_LAYOUT;
-        }
-
-        if (mShowingDream != mLastShowingDream) {
-            mLastShowingDream = mShowingDream;
-            mWindowManagerFuncs.notifyShowingDreamChanged();
-        }
-
-        updateWindowSleepToken();
-
-        // update since mAllowLockscreenWhenOn might have changed
-        updateLockScreenTimeout();
-        return changes;
-    }
-
-    private void updateWindowSleepToken() {
-        if (mWindowSleepTokenNeeded && !mLastWindowSleepTokenNeeded) {
-            mHandler.removeCallbacks(mReleaseSleepTokenRunnable);
-            mHandler.post(mAcquireSleepTokenRunnable);
-        } else if (!mWindowSleepTokenNeeded && mLastWindowSleepTokenNeeded) {
-            mHandler.removeCallbacks(mAcquireSleepTokenRunnable);
-            mHandler.post(mReleaseSleepTokenRunnable);
-        }
-        mLastWindowSleepTokenNeeded = mWindowSleepTokenNeeded;
-    }
-
-    /**
-     * @return Whether the top app should hide the statusbar based on the top fullscreen opaque
-     *         window.
-     */
-    private boolean topAppHidesStatusBar() {
-        if (mTopFullscreenOpaqueWindowState == null) {
-            return false;
-        }
-        final int fl = PolicyControl.getWindowFlags(null,
-                mTopFullscreenOpaqueWindowState.getAttrs());
-        if (localLOGV) {
-            Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw());
-            Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
-                    + " lp.flags=0x" + Integer.toHexString(fl));
-        }
-        return (fl & LayoutParams.FLAG_FULLSCREEN) != 0
-                || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+    public void setKeyguardCandidateLw(WindowState win) {
+        mKeyguardCandidate = win;
+        setKeyguardOccludedLw(mKeyguardOccluded, true /* force */);
     }
 
     /**
@@ -5598,19 +3391,19 @@
         if (!isOccluded && changed && showing) {
             mKeyguardOccluded = false;
             mKeyguardDelegate.setOccluded(false, true /* animate */);
-            if (mStatusBar != null) {
-                mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD;
+            if (mKeyguardCandidate != null) {
+                mKeyguardCandidate.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD;
                 if (!mKeyguardDelegate.hasLockscreenWallpaper()) {
-                    mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+                    mKeyguardCandidate.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
                 }
             }
             return true;
         } else if (isOccluded && changed && showing) {
             mKeyguardOccluded = true;
             mKeyguardDelegate.setOccluded(true, false /* animate */);
-            if (mStatusBar != null) {
-                mStatusBar.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD;
-                mStatusBar.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
+            if (mKeyguardCandidate != null) {
+                mKeyguardCandidate.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD;
+                mKeyguardCandidate.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
             }
             return true;
         } else if (changed) {
@@ -5622,28 +3415,6 @@
         }
     }
 
-    private boolean isStatusBarKeyguard() {
-        return mStatusBar != null
-                && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
-    }
-
-    @Override
-    public boolean allowAppAnimationsLw() {
-        return !mShowingDream;
-    }
-
-    @Override
-    public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
-        mFocusedWindow = newFocus;
-        mLastFocusedWindow = lastFocus;
-        if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
-            // If the navigation bar has been hidden or shown, we need to do another
-            // layout pass to update that window.
-            return FINISH_LAYOUT_REDO_LAYOUT;
-        }
-        return 0;
-    }
-
     /** {@inheritDoc} */
     @Override
     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
@@ -6469,57 +4240,11 @@
                 // current user.
                 mSettingsObserver.onChange(false);
                 mDefaultDisplayRotation.onUserSwitch();
-
-                // force a re-application of focused window sysui visibility.
-                // the window may never have been shown for this user
-                // e.g. the keyguard when going through the new-user setup flow
-                synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
-                    mLastSystemUiFlags = 0;
-                    updateSystemUiVisibilityLw();
-                }
+                mWindowManagerFuncs.onUserSwitched();
             }
         }
     };
 
-    private final Runnable mHiddenNavPanic = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
-                if (!isUserSetupComplete()) {
-                    // Swipe-up for navigation bar is disabled during setup
-                    return;
-                }
-                mPendingPanicGestureUptime = SystemClock.uptimeMillis();
-                if (!isNavBarEmpty(mLastSystemUiFlags)) {
-                    mNavigationBarController.showTransient();
-                }
-            }
-        }
-    };
-
-    private void requestTransientBars(WindowState swipeTarget) {
-        synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
-            if (!isUserSetupComplete()) {
-                // Swipe-up for navigation bar is disabled during setup
-                return;
-            }
-            boolean sb = mStatusBarController.checkShowTransientBarLw();
-            boolean nb = mNavigationBarController.checkShowTransientBarLw()
-                    && !isNavBarEmpty(mLastSystemUiFlags);
-            if (sb || nb) {
-                // Don't show status bar when swiping on already visible navigation bar
-                if (!nb && swipeTarget == mNavigationBar) {
-                    if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
-                    return;
-                }
-                if (sb) mStatusBarController.showTransient();
-                if (nb) mNavigationBarController.showTransient();
-                mImmersiveModeConfirmation.confirmCurrentPrompt();
-                updateSystemUiVisibilityLw();
-            }
-        }
-    }
-
     // Called on the PowerManager's Notifier thread.
     @Override
     public void startedGoingToSleep(int why) {
@@ -6852,11 +4577,6 @@
     }
 
     @Override
-    public boolean isShowingDreamLw() {
-        return mShowingDream;
-    }
-
-    @Override
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
         if (mKeyguardDelegate != null) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.startKeyguardExitAnimation");
@@ -6864,85 +4584,6 @@
         }
     }
 
-    @Override
-    public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
-            DisplayCutout displayCutout, Rect outInsets) {
-        outInsets.setEmpty();
-
-        // Navigation bar and status bar.
-        getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets);
-        outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]);
-    }
-
-    @Override
-    public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
-            DisplayCutout displayCutout, Rect outInsets) {
-        outInsets.setEmpty();
-
-        // Only navigation bar
-        if (mDefaultDisplayPolicy.hasNavigationBar()) {
-            int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
-            if (position == NAV_BAR_BOTTOM) {
-                outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
-            } else if (position == NAV_BAR_RIGHT) {
-                outInsets.right = getNavigationBarWidth(displayRotation, mUiMode);
-            } else if (position == NAV_BAR_LEFT) {
-                outInsets.left = getNavigationBarWidth(displayRotation, mUiMode);
-            }
-        }
-
-        if (displayCutout != null) {
-            outInsets.left += displayCutout.getSafeInsetLeft();
-            outInsets.top += displayCutout.getSafeInsetTop();
-            outInsets.right += displayCutout.getSafeInsetRight();
-            outInsets.bottom += displayCutout.getSafeInsetBottom();
-        }
-    }
-
-    @Override
-    public boolean isNavBarForcedShownLw(WindowState windowState) {
-        return mForceShowSystemBars;
-    }
-
-    @Override
-    public int getNavBarPosition() {
-        // TODO(multi-display): Support system decor on secondary displays.
-        return mNavigationBarPosition;
-    }
-
-    @Override
-    public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
-            int displayHeight, int displayRotation) {
-        final int barPosition = navigationBarPosition(displayWidth, displayHeight, displayRotation);
-        return isDockSideAllowed(dockSide, originalDockSide, barPosition,
-                mDefaultDisplayPolicy.navigationBarCanMove());
-    }
-
-    @VisibleForTesting
-    static boolean isDockSideAllowed(int dockSide, int originalDockSide,
-            int navBarPosition, boolean navigationBarCanMove) {
-        if (dockSide == DOCKED_TOP) {
-            return true;
-        }
-
-        if (navigationBarCanMove) {
-            // Only allow the dockside opposite to the nav bar position in landscape
-            return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT
-                    || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT;
-        }
-
-        // Side is the same as original side
-        if (dockSide == originalDockSide) {
-            return true;
-        }
-
-        // Only if original docked side was top in portrait will allow left for landscape
-        if (dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP) {
-            return true;
-        }
-        return false;
-    }
-
     void sendCloseSystemWindows() {
         PhoneWindow.sendCloseSystemWindows(mContext, null);
     }
@@ -7009,9 +4650,6 @@
             }
         }
 
-        mSystemGestures.systemReady();
-        mImmersiveModeConfirmation.systemReady();
-
         mAutofillManagerInternal = LocalServices.getService(AutofillManagerInternal.class);
     }
 
@@ -7173,10 +4811,22 @@
         mHandler.post(mScreenLockTimeout);
     }
 
+    // TODO (b/113840485): Move this logic to DisplayPolicy when lockscreen supports multi-display.
+    @Override
+    public void setAllowLockscreenWhenOn(int displayId, boolean allow) {
+        if (allow) {
+            mAllowLockscreenWhenOnDisplays.add(displayId);
+        } else {
+            mAllowLockscreenWhenOnDisplays.remove(displayId);
+        }
+        updateLockScreenTimeout();
+    }
+
     private void updateLockScreenTimeout() {
         synchronized (mScreenLockTimeout) {
-            final boolean enable = (mAllowLockscreenWhenOn && mDefaultDisplayPolicy.isAwake() &&
-                    mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId));
+            final boolean enable = !mAllowLockscreenWhenOnDisplays.isEmpty()
+                    && mDefaultDisplayPolicy.isAwake()
+                    && mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId);
             if (mLockScreenTimerActive != enable) {
                 if (enable) {
                     if (localLOGV) Log.v(TAG, "setting lockscreen timer");
@@ -7192,21 +4842,6 @@
     }
 
     // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
-    private void updateDreamingSleepToken(boolean acquire) {
-        if (acquire) {
-            if (mDreamingSleepToken == null) {
-                mDreamingSleepToken = mActivityTaskManagerInternal.acquireSleepToken(
-                        "Dream", DEFAULT_DISPLAY);
-            }
-        } else {
-            if (mDreamingSleepToken != null) {
-                mDreamingSleepToken.release();
-                mDreamingSleepToken = null;
-            }
-        }
-    }
-
-    // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
     private void updateScreenOffSleepToken(boolean acquire) {
         if (acquire) {
             if (mScreenOffSleepToken == null) {
@@ -7254,6 +4889,11 @@
         }
     }
 
+    @Override
+    public int getUiMode() {
+        return mUiMode;
+    }
+
     void updateRotation(boolean alwaysSendConfiguration) {
         try {
             // Set orientation on WindowManager.
@@ -7510,380 +5150,6 @@
         }
     }
 
-    private int updateSystemUiVisibilityLw() {
-        // If there is no window focused, there will be nobody to handle the events
-        // anyway, so just hang on in whatever state we're in until things settle down.
-        WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
-                : mTopFullscreenOpaqueWindowState;
-        if (winCandidate == null) {
-            return 0;
-        }
-
-        // The immersive mode confirmation should never affect the system bar visibility, otherwise
-        // it will unhide the navigation bar and hide itself.
-        if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
-
-            // The immersive mode confirmation took the focus from mLastFocusedWindow which was
-            // controlling the system ui visibility. So if mLastFocusedWindow can still receive
-            // keys, we let it keep controlling the visibility.
-            final boolean lastFocusCanReceiveKeys =
-                    (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
-            winCandidate = isStatusBarKeyguard() ? mStatusBar
-                    : lastFocusCanReceiveKeys ? mLastFocusedWindow
-                    : mTopFullscreenOpaqueWindowState;
-            if (winCandidate == null) {
-                return 0;
-            }
-        }
-        final WindowState win = winCandidate;
-        if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mKeyguardOccluded) {
-            // We are updating at a point where the keyguard has gotten
-            // focus, but we were last in a state where the top window is
-            // hiding it.  This is probably because the keyguard as been
-            // shown while the top window was displayed, so we want to ignore
-            // it here because this is just a very transient change and it
-            // will quickly lose focus once it correctly gets hidden.
-            return 0;
-        }
-
-        int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
-                & ~mResettingSystemUiFlags
-                & ~mForceClearedSystemUiFlags;
-        if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
-            tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
-        }
-
-        final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
-                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
-        final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
-                mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
-        mWindowManagerFuncs.getStackBounds(
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
-        mWindowManagerFuncs.getStackBounds(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
-        final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
-        final int diff = visibility ^ mLastSystemUiFlags;
-        final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
-        final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
-        final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
-        if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
-                && mFocusedApp == win.getAppToken()
-                && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
-                && mLastDockedStackBounds.equals(mDockedStackBounds)) {
-            return 0;
-        }
-        mLastSystemUiFlags = visibility;
-        mLastFullscreenStackSysUiFlags = fullscreenVisibility;
-        mLastDockedStackSysUiFlags = dockedVisibility;
-        mLastFocusNeedsMenu = needsMenu;
-        mFocusedApp = win.getAppToken();
-        final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
-        final Rect dockedStackBounds = new Rect(mDockedStackBounds);
-        mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
-                    if (statusbar != null) {
-                        statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
-                                dockedVisibility, 0xffffffff, fullscreenStackBounds,
-                                dockedStackBounds, win.toString());
-                        statusbar.topAppWindowChanged(needsMenu);
-                    }
-                }
-            });
-        return diff;
-    }
-
-    private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
-        final boolean onKeyguard = isStatusBarKeyguard() && !mKeyguardOccluded;
-        final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming;
-        if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) {
-            // If the top fullscreen-or-dimming window is also the top fullscreen, respect
-            // its light flag.
-            vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-            vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
-                    & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-        } else if (statusColorWin != null && statusColorWin.isDimming()) {
-            // Otherwise if it's dimming, clear the light flag.
-            vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-        }
-        return vis;
-    }
-
-    @VisibleForTesting
-    @Nullable
-    static WindowState chooseNavigationColorWindowLw(WindowState opaque,
-            WindowState opaqueOrDimming, WindowState imeWindow,
-            @NavigationBarPosition int navBarPosition) {
-        // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME
-        // window can be navigation color window.
-        final boolean imeWindowCanNavColorWindow = imeWindow != null
-                && imeWindow.isVisibleLw()
-                && navBarPosition == NAV_BAR_BOTTOM
-                && (PolicyControl.getWindowFlags(imeWindow, null)
-                & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
-
-        if (opaque != null && opaqueOrDimming == opaque) {
-            // If the top fullscreen-or-dimming window is also the top fullscreen, respect it
-            // unless IME window is also eligible, since currently the IME window is always show
-            // above the opaque fullscreen app window, regardless of the IME target window.
-            // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.
-            return imeWindowCanNavColorWindow ? imeWindow : opaque;
-        }
-
-        if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {
-            // No dimming window is involved. Determine the result only with the IME window.
-            return imeWindowCanNavColorWindow ? imeWindow : null;
-        }
-
-        if (!imeWindowCanNavColorWindow) {
-            // No IME window is involved. Determine the result only with opaqueOrDimming.
-            return opaqueOrDimming;
-        }
-
-        // The IME window and the dimming window are competing.  Check if the dimming window can be
-        // IME target or not.
-        if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) {
-            // The IME window is above the dimming window.
-            return imeWindow;
-        } else {
-            // The dimming window is above the IME window.
-            return opaqueOrDimming;
-        }
-    }
-
-    @VisibleForTesting
-    static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming,
-            WindowState imeWindow, WindowState navColorWin) {
-
-        if (navColorWin != null) {
-            if (navColorWin == imeWindow || navColorWin == opaque) {
-                // Respect the light flag.
-                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-                vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
-                        & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-            } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
-                // Clear the light flag for dimming window.
-                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-            }
-        }
-        return vis;
-    }
-
-    private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
-        final boolean dockedStackVisible =
-                mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        final boolean freeformStackVisible =
-                mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM);
-        final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();
-
-        // We need to force system bars when the docked stack is visible, when the freeform stack
-        // is visible but also when we are resizing for the transitions when docked stack
-        // visibility changes.
-        mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
-        final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
-
-        // apply translucent bar vis flags
-        WindowState fullscreenTransWin = isStatusBarKeyguard() && !mKeyguardOccluded
-                ? mStatusBar
-                : mTopFullscreenOpaqueWindowState;
-        vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
-        vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
-        final int dockedVis = mStatusBarController.applyTranslucentFlagLw(
-                mTopDockedOpaqueWindowState, 0, 0);
-
-        final boolean fullscreenDrawsStatusBarBackground =
-                drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);
-        final boolean dockedDrawsStatusBarBackground =
-                drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);
-
-        // prevent status bar interaction from clearing certain flags
-        int type = win.getAttrs().type;
-        boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
-        if (statusBarHasFocus && !isStatusBarKeyguard()) {
-            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
-                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
-                    | View.SYSTEM_UI_FLAG_IMMERSIVE
-                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
-                    | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-            if (mKeyguardOccluded) {
-                flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
-            }
-            vis = (vis & ~flags) | (oldVis & flags);
-        }
-
-        if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
-            vis |= View.STATUS_BAR_TRANSPARENT;
-            vis &= ~View.STATUS_BAR_TRANSLUCENT;
-        } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)
-                || forceOpaqueStatusBar) {
-            vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
-        }
-
-        vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);
-
-        // update status bar
-        boolean immersiveSticky =
-                (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
-        final boolean hideStatusBarWM =
-                mTopFullscreenOpaqueWindowState != null
-                && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
-                        & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
-        final boolean hideStatusBarSysui =
-                (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
-        final boolean hideNavBarSysui =
-                (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
-
-        final boolean transientStatusBarAllowed = mStatusBar != null
-                && (statusBarHasFocus || (!mForceShowSystemBars
-                        && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
-
-        final boolean transientNavBarAllowed = mNavigationBar != null
-                && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;
-
-        final long now = SystemClock.uptimeMillis();
-        final boolean pendingPanic = mPendingPanicGestureUptime != 0
-                && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
-        if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard()
-                && mDefaultDisplayPolicy.isKeyguardDrawComplete()) {
-            // The user performed the panic gesture recently, we're about to hide the bars,
-            // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
-            mPendingPanicGestureUptime = 0;
-            mStatusBarController.showTransient();
-            if (!isNavBarEmpty(vis)) {
-                mNavigationBarController.showTransient();
-            }
-        }
-
-        final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
-                && !transientStatusBarAllowed && hideStatusBarSysui;
-        final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
-                && !transientNavBarAllowed;
-        if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {
-            // clear the clearable flags instead
-            clearClearableFlagsLw();
-            vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
-        }
-
-        final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
-        immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
-        final boolean navAllowedHidden = immersive || immersiveSticky;
-
-        if (hideNavBarSysui && !navAllowedHidden
-                && getWindowLayerLw(win) > getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) {
-            // We can't hide the navbar from this window otherwise the input consumer would not get
-            // the input events.
-            vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
-        }
-
-        vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);
-
-        // update navigation bar
-        boolean oldImmersiveMode = isImmersiveMode(oldVis);
-        boolean newImmersiveMode = isImmersiveMode(vis);
-        if (win != null && oldImmersiveMode != newImmersiveMode) {
-            final String pkg = win.getOwningPackage();
-            mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
-                    isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility()));
-        }
-
-        vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
-
-        final WindowState navColorWin = chooseNavigationColorWindowLw(
-                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
-                mWindowManagerFuncs.getInputMethodWindowLw(), mNavigationBarPosition);
-        vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
-                mTopFullscreenOpaqueOrDimmingWindowState,
-                mWindowManagerFuncs.getInputMethodWindowLw(), navColorWin);
-
-        return vis;
-    }
-
-    private boolean drawsStatusBarBackground(int vis, WindowState win) {
-        if (!mStatusBarController.isTransparentAllowed(win)) {
-            return false;
-        }
-        if (win == null) {
-            return true;
-        }
-
-        final boolean drawsSystemBars =
-                (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
-        final boolean forceDrawsSystemBars =
-                (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
-
-        return forceDrawsSystemBars || drawsSystemBars && (vis & View.STATUS_BAR_TRANSLUCENT) == 0;
-    }
-
-    /**
-     * @return the current visibility flags with the nav-bar opacity related flags toggled based
-     *         on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
-     */
-    private int configureNavBarOpacity(int visibility, boolean dockedStackVisible,
-            boolean freeformStackVisible, boolean isDockedDividerResizing) {
-        if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
-            if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) {
-                visibility = setNavBarOpaqueFlag(visibility);
-            }
-        } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
-            if (isDockedDividerResizing) {
-                visibility = setNavBarOpaqueFlag(visibility);
-            } else if (freeformStackVisible) {
-                visibility = setNavBarTranslucentFlag(visibility);
-            } else {
-                visibility = setNavBarOpaqueFlag(visibility);
-            }
-        }
-
-        if (!areTranslucentBarsAllowed()) {
-            visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT;
-        }
-        return visibility;
-    }
-
-    private int setNavBarOpaqueFlag(int visibility) {
-        return visibility &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT);
-    }
-
-    private int setNavBarTranslucentFlag(int visibility) {
-        visibility &= ~View.NAVIGATION_BAR_TRANSPARENT;
-        return visibility |= View.NAVIGATION_BAR_TRANSLUCENT;
-    }
-
-    private void clearClearableFlagsLw() {
-        int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS;
-        if (newVal != mResettingSystemUiFlags) {
-            mResettingSystemUiFlags = newVal;
-            mWindowManagerFuncs.reevaluateStatusBarVisibility();
-        }
-    }
-
-    private boolean isImmersiveMode(int vis) {
-        final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
-        return mNavigationBar != null
-                && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
-                && (vis & flags) != 0
-                && canHideNavigationBar();
-    }
-
-    private static boolean isNavBarEmpty(int systemUiFlags) {
-        final int disableNavigationBar = (View.STATUS_BAR_DISABLE_HOME
-                | View.STATUS_BAR_DISABLE_BACK
-                | View.STATUS_BAR_DISABLE_RECENT);
-
-        return (systemUiFlags & disableNavigationBar) == disableNavigationBar;
-    }
-
-    /**
-     * @return whether the navigation or status bar can be made translucent
-     *
-     * This should return true unless touch exploration is not enabled or
-     * R.boolean.config_enableTranslucentDecor is false.
-     */
-    private boolean areTranslucentBarsAllowed() {
-        return mTranslucentDecorEnabled;
-    }
-
     // Use this instead of checking config_showNavigationBar so that it can be consistently
     // overridden by qemu.hw.mainkeys in the emulator.
     @Override
@@ -7926,44 +5192,8 @@
     }
 
     @Override
-    public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation,
-            int newRotation) {
-        // For the upside down rotation we don't rotate seamlessly as the navigation
-        // bar moves position.
-        // Note most apps (using orientation:sensor or user as opposed to fullSensor)
-        // will not enter the reverse portrait orientation, so actually the
-        // orientation won't change at all.
-        if (oldRotation == displayRotation.getUpsideDownRotation()
-                || newRotation == displayRotation.getUpsideDownRotation()) {
-            return false;
-        }
-        // If the navigation bar can't change sides, then it will
-        // jump when we change orientations and we don't rotate
-        // seamlessly.
-        if (!displayRotation.getDisplayPolicy().navigationBarCanMove()) {
-            return false;
-        }
-
-        final WindowState w = mTopFullscreenOpaqueWindowState;
-        if (w != mFocusedWindow) {
-            return false;
-        }
-
-        // We only enable seamless rotation if the top window has requested
-        // it and is in the fullscreen opaque state. Seamless rotation
-        // requires freezing various Surface states and won't work well
-        // with animations, so we disable it in the animation case for now.
-        if (w != null && !w.isAnimatingLw() &&
-                w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        proto.write(LAST_SYSTEM_UI_FLAGS, mLastSystemUiFlags);
         proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
         proto.write(ROTATION, mDefaultDisplayRotation.getUserRotation());
         proto.write(ORIENTATION, mDefaultDisplayRotation.getCurrentAppOrientation());
@@ -7971,30 +5201,9 @@
         proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete());
         proto.write(WINDOW_MANAGER_DRAW_COMPLETE,
                 mDefaultDisplayPolicy.isWindowManagerDrawComplete());
-        if (mFocusedApp != null) {
-            proto.write(FOCUSED_APP_TOKEN, mFocusedApp.toString());
-        }
-        if (mFocusedWindow != null) {
-            mFocusedWindow.writeIdentifierToProto(proto, FOCUSED_WINDOW);
-        }
-        if (mTopFullscreenOpaqueWindowState != null) {
-            mTopFullscreenOpaqueWindowState.writeIdentifierToProto(
-                    proto, TOP_FULLSCREEN_OPAQUE_WINDOW);
-        }
-        if (mTopFullscreenOpaqueOrDimmingWindowState != null) {
-            mTopFullscreenOpaqueOrDimmingWindowState.writeIdentifierToProto(
-                    proto, TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW);
-        }
         proto.write(KEYGUARD_OCCLUDED, mKeyguardOccluded);
         proto.write(KEYGUARD_OCCLUDED_CHANGED, mKeyguardOccludedChanged);
         proto.write(KEYGUARD_OCCLUDED_PENDING, mPendingKeyguardOccluded);
-        proto.write(FORCE_STATUS_BAR, mForceStatusBar);
-        proto.write(FORCE_STATUS_BAR_FROM_KEYGUARD, mForceStatusBarFromKeyguard);
-        mStatusBarController.writeToProto(proto, STATUS_BAR);
-        mNavigationBarController.writeToProto(proto, NAVIGATION_BAR);
-        if (mDefaultOrientationListener != null) {
-            mDefaultOrientationListener.writeToProto(proto, ORIENTATION_LISTENER);
-        }
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.writeToProto(proto, KEYGUARD_DELEGATE);
         }
@@ -8008,19 +5217,6 @@
                 pw.print(" mSystemBooted="); pw.println(mSystemBooted);
         pw.print(prefix); pw.print("mCameraLensCoverState=");
                 pw.println(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
-        if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0
-                || mForceClearedSystemUiFlags != 0) {
-            pw.print(prefix); pw.print("mLastSystemUiFlags=0x");
-                    pw.print(Integer.toHexString(mLastSystemUiFlags));
-                    pw.print(" mResettingSystemUiFlags=0x");
-                    pw.print(Integer.toHexString(mResettingSystemUiFlags));
-                    pw.print(" mForceClearedSystemUiFlags=0x");
-                    pw.println(Integer.toHexString(mForceClearedSystemUiFlags));
-        }
-        if (mLastFocusNeedsMenu) {
-            pw.print(prefix); pw.print("mLastFocusNeedsMenu=");
-                    pw.println(mLastFocusNeedsMenu);
-        }
         pw.print(prefix); pw.print("mWakeGestureEnabledSetting=");
                 pw.println(mWakeGestureEnabledSetting);
 
@@ -8083,50 +5279,11 @@
             final int key = mDisplayHomeButtonHandlers.keyAt(i);
             pw.println(mDisplayHomeButtonHandlers.get(key));
         }
-        pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer);
-                pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer);
-        pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
-                pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
-                pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
-        if (mStatusBar != null) {
-            pw.print(prefix); pw.print("mStatusBar=");
-                    pw.print(mStatusBar); pw.print(" isStatusBarKeyguard=");
-                    pw.println(isStatusBarKeyguard());
-        }
-        if (mNavigationBar != null) {
-            pw.print(prefix); pw.print("mNavigationBar=");
-                    pw.println(mNavigationBar);
-        }
-        if (mFocusedWindow != null) {
-            pw.print(prefix); pw.print("mFocusedWindow=");
-                    pw.println(mFocusedWindow);
-        }
-        if (mFocusedApp != null) {
-            pw.print(prefix); pw.print("mFocusedApp=");
-                    pw.println(mFocusedApp);
-        }
-        if (mTopFullscreenOpaqueWindowState != null) {
-            pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState=");
-                    pw.println(mTopFullscreenOpaqueWindowState);
-        }
-        if (mTopFullscreenOpaqueOrDimmingWindowState != null) {
-            pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState=");
-                    pw.println(mTopFullscreenOpaqueOrDimmingWindowState);
-        }
-        if (mForcingShowNavBar) {
-            pw.print(prefix); pw.print("mForcingShowNavBar=");
-                    pw.println(mForcingShowNavBar); pw.print( "mForcingShowNavBarLayer=");
-                    pw.println(mForcingShowNavBarLayer);
-        }
-        pw.print(prefix); pw.print("mTopIsFullscreen="); pw.print(mTopIsFullscreen);
-                pw.print(" mKeyguardOccluded="); pw.println(mKeyguardOccluded);
-        pw.print(prefix);
-                pw.print("mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged);
+        pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(mKeyguardOccluded);
+                pw.print(" mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged);
                 pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded);
-        pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
-                pw.print(" mForceStatusBarFromKeyguard=");
-                pw.println(mForceStatusBarFromKeyguard);
-        pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn);
+        pw.print(prefix); pw.print("mAllowLockscreenWhenOnDisplays=");
+                pw.print(!mAllowLockscreenWhenOnDisplays.isEmpty());
                 pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
                 pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
         if (mHasFeatureLeanback) {
@@ -8139,16 +5296,10 @@
         }
 
         mGlobalKeyManager.dump(prefix, pw);
-        mStatusBarController.dump(pw, prefix);
-        mNavigationBarController.dump(pw, prefix);
-        PolicyControl.dump(prefix, pw);
 
         if (mWakeGestureListener != null) {
             mWakeGestureListener.dump(pw, prefix);
         }
-        if (mDefaultOrientationListener != null) {
-            mDefaultOrientationListener.dump(pw, prefix);
-        }
         if (mBurnInProtectionHelper != null) {
             mBurnInProtectionHelper.dump(prefix, pw);
         }
@@ -8310,11 +5461,6 @@
     }
 
     @Override
-    public void onLockTaskStateChangedLw(int lockTaskState) {
-        mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState);
-    }
-
-    @Override
     public boolean setAodShowing(boolean aodShowing) {
         if (mAodShowing != aodShowing) {
             mAodShowing = aodShowing;
diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java
deleted file mode 100644
index e6e4d7f..0000000
--- a/services/core/java/com/android/server/policy/StatusBarController.java
+++ /dev/null
@@ -1,127 +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 com.android.server.policy;
-
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
-import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
-import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-
-import android.app.StatusBarManager;
-import android.os.IBinder;
-import android.os.SystemClock;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.Interpolator;
-import android.view.animation.TranslateAnimation;
-
-import com.android.server.LocalServices;
-import com.android.server.statusbar.StatusBarManagerInternal;
-
-/**
- * Implements status bar specific behavior.
- */
-public class StatusBarController extends BarController {
-
-    private final AppTransitionListener mAppTransitionListener
-            = new AppTransitionListener() {
-
-        @Override
-        public void onAppTransitionPendingLocked() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    StatusBarManagerInternal statusbar = getStatusBarInternal();
-                    if (statusbar != null) {
-                        statusbar.appTransitionPending();
-                    }
-                }
-            });
-        }
-
-        @Override
-        public int onAppTransitionStartingLocked(int transit, IBinder openToken,
-                IBinder closeToken, long duration, long statusBarAnimationStartTime,
-                long statusBarAnimationDuration) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    StatusBarManagerInternal statusbar = getStatusBarInternal();
-                    if (statusbar != null) {
-                        statusbar.appTransitionStarting(statusBarAnimationStartTime,
-                                statusBarAnimationDuration);
-                    }
-                }
-            });
-            return 0;
-        }
-
-        @Override
-        public void onAppTransitionCancelledLocked(int transit) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    StatusBarManagerInternal statusbar = getStatusBarInternal();
-                    if (statusbar != null) {
-                        statusbar.appTransitionCancelled();
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void onAppTransitionFinishedLocked(IBinder token) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    StatusBarManagerInternal statusbar = LocalServices.getService(
-                            StatusBarManagerInternal.class);
-                    if (statusbar != null) {
-                        statusbar.appTransitionFinished();
-                    }
-                }
-            });
-        }
-    };
-
-    public StatusBarController() {
-        super("StatusBar",
-                View.STATUS_BAR_TRANSIENT,
-                View.STATUS_BAR_UNHIDE,
-                View.STATUS_BAR_TRANSLUCENT,
-                StatusBarManager.WINDOW_STATUS_BAR,
-                FLAG_TRANSLUCENT_STATUS,
-                View.STATUS_BAR_TRANSPARENT);
-    }
-
-
-    public void setTopAppHidesStatusBar(boolean hidesStatusBar) {
-        StatusBarManagerInternal statusbar = getStatusBarInternal();
-        if (statusbar != null) {
-            statusbar.setTopAppHidesStatusBar(hidesStatusBar);
-        }
-    }
-
-    @Override
-    protected boolean skipAnimation() {
-        return mWin.getAttrs().height == MATCH_PARENT;
-    }
-
-    public AppTransitionListener getAppTransitionListener() {
-        return mAppTransitionListener;
-    }
-}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e1c4acf..3d474e3 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -65,7 +65,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.res.CompatibilityInfo;
@@ -78,7 +77,6 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
-import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.IWindowManager;
 import android.view.InputEventReceiver;
@@ -90,7 +88,6 @@
 
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
-import com.android.server.wm.DisplayFrames;
 import com.android.server.wm.DisplayRotation;
 import com.android.server.wm.WindowFrames;
 
@@ -173,11 +170,6 @@
     void onKeyguardOccludedChangedLw(boolean occluded);
 
     /**
-     * Called when the resource overlays change.
-     */
-    default void onOverlayChangedLw(DisplayContentInfo displayContentInfo) {}
-
-    /**
      * Interface to the Window Manager state associated with a particular
      * window.  You can hold on to an instance of this interface from the call
      * to prepareAddWindow() until removeWindow().
@@ -526,11 +518,6 @@
         public static final int CAMERA_LENS_COVERED = 1;
 
         /**
-         * Ask the window manager to re-evaluate the system UI flags.
-         */
-        public void reevaluateStatusBarVisibility();
-
-        /**
          * Add a input consumer which will consume all input events going to any window below it.
          */
         public InputConsumer createInputConsumer(Looper looper, String name,
@@ -573,22 +560,12 @@
         void unregisterPointerEventListener(PointerEventListener listener, int displayId);
 
         /**
-         * @return The content insets of the docked divider window.
-         */
-        int getDockedDividerInsetsLw();
-
-        /**
          * Retrieves the {@param outBounds} from the stack matching the {@param windowingMode} and
          * {@param activityType}.
          */
         void getStackBounds(int windowingMode, int activityType, Rect outBounds);
 
         /**
-         * Notifies window manager that {@link #isShowingDreamLw} has changed.
-         */
-        void notifyShowingDreamChanged();
-
-        /**
          * @return The currently active input method window.
          */
         WindowState getInputMethodWindowLw();
@@ -648,7 +625,15 @@
          */
         void onKeyguardShowingAndNotOccludedChanged();
 
-        DisplayContentInfo getDefaultDisplayContentInfo();
+        /**
+         * Notifies window manager that power key is being pressed.
+         */
+        void onPowerKeyDown(boolean isScreenOn);
+
+        /**
+         * Notifies window manager that user is switched.
+         */
+        void onUserSwitched();
     }
 
     /**
@@ -735,17 +720,6 @@
     public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs);
 
     /**
-     * Sanitize the layout parameters coming from a client.  Allows the policy
-     * to do things like ensure that windows of a specific type can't take
-     * input focus.
-     *
-     * @param attrs The window layout parameters to be modified.  These values
-     * are modified in-place.
-     */
-    public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
-            boolean hasStatusBarServicePermission);
-
-    /**
      * After the window manager has computed the current configuration based
      * on its knowledge of the display and input devices, it gives the policy
      * a chance to adjust the information contained in it.  If you want to
@@ -941,40 +915,6 @@
     public int getMaxWallpaperLayer();
 
     /**
-     * Return the display width available after excluding any screen
-     * decorations that could never be removed in Honeycomb. That is, system bar or
-     * button bar.
-     */
-    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation,
-            int uiMode, int displayId, DisplayCutout displayCutout);
-
-    /**
-     * Return the display height available after excluding any screen
-     * decorations that could never be removed in Honeycomb. That is, system bar or
-     * button bar.
-     */
-    public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation,
-            int uiMode, int displayId, DisplayCutout displayCutout);
-
-    /**
-     * Return the available screen width that we should report for the
-     * configuration.  This must be no larger than
-     * {@link #getNonDecorDisplayWidth(int, int, int, int, int, DisplayCutout)}; it may be smaller
-     * than that to account for more transient decoration like a status bar.
-     */
-    public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation,
-            int uiMode, int displayId, DisplayCutout displayCutout);
-
-    /**
-     * Return the available screen height that we should report for the
-     * configuration.  This must be no larger than
-     * {@link #getNonDecorDisplayHeight(int, int, int, int, int, DisplayCutout)}; it may be smaller
-     * than that to account for more transient decoration like a status bar.
-     */
-    public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation,
-            int uiMode, int displayId, DisplayCutout displayCutout);
-
-    /**
      * Return whether the given window can become the Keyguard window. Typically returns true for
      * the StatusBar.
      */
@@ -1013,65 +953,11 @@
             int logo, int windowFlags, Configuration overrideConfig, int displayId);
 
     /**
-     * Prepare for a window being added to the window manager.  You can throw an
-     * exception here to prevent the window being added, or do whatever setup
-     * you need to keep track of the window.
+     * Set or clear a window which can behave as the keyguard.
      *
-     * @param win The window being added.
-     * @param attrs The window's LayoutParams.
-     *
-     * @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed, else an
-     *         error code to abort the add.
+     * @param win The window which can behave as the keyguard.
      */
-    public int prepareAddWindowLw(WindowState win,
-            WindowManager.LayoutParams attrs);
-
-    /**
-     * Called when a window is being removed from a window manager.  Must not
-     * throw an exception -- clean up as much as possible.
-     *
-     * @param win The window being removed.
-     */
-    public void removeWindowLw(WindowState win);
-
-    /**
-     * Control the animation to run when a window's state changes.  Return a
-     * non-0 number to force the animation to a specific resource ID, or 0
-     * to use the default animation.
-     *
-     * @param win The window that is changing.
-     * @param transit What is happening to the window: {@link #TRANSIT_ENTER},
-     *                {@link #TRANSIT_EXIT}, {@link #TRANSIT_SHOW}, or
-     *                {@link #TRANSIT_HIDE}.
-     *
-     * @return Resource ID of the actual animation to use, or 0 for none.
-     */
-    public int selectAnimationLw(WindowState win, int transit);
-
-    /**
-     * Determine the animation to run for a rotation transition based on the
-     * top fullscreen windows {@link WindowManager.LayoutParams#rotationAnimation}
-     * and whether it is currently fullscreen and frontmost.
-     *
-     * @param anim The exiting animation resource id is stored in anim[0], the
-     * entering animation resource id is stored in anim[1].
-     */
-    public void selectRotationAnimationLw(int anim[]);
-
-    /**
-     * Validate whether the current top fullscreen has specified the same
-     * {@link WindowManager.LayoutParams#rotationAnimation} value as that
-     * being passed in from the previous top fullscreen window.
-     *
-     * @param exitAnimId exiting resource id from the previous window.
-     * @param enterAnimId entering resource id from the previous window.
-     * @param forceDefault For rotation animations only, if true ignore the
-     * animation values and just return false.
-     * @return true if the previous values are still valid, false if they
-     * should be replaced with the default.
-     */
-    public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId,
-            boolean forceDefault);
+    void setKeyguardCandidateLw(@Nullable WindowState win);
 
     /**
      * Create and return an animation to re-display a window that was force hidden by Keyguard.
@@ -1148,100 +1034,21 @@
     public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags);
 
     /**
-     * Called when layout of the windows is about to start.
+     * Apply the keyguard policy to a specific window.
      *
-     * @param displayFrames frames of the display we are doing layout on.
-     * @param uiMode The current uiMode in configuration.
+     * @param win The window to apply the keyguard policy.
+     * @param imeTarget The current IME target window.
      */
-    default void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {}
+    void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget);
 
     /**
-     * Returns the bottom-most layer of the system decor, above which no policy decor should
-     * be applied.
-     */
-    public int getSystemDecorLayerLw();
-
-    /**
-     * Called for each window attached to the window manager as layout is proceeding. The
-     * implementation of this function must take care of setting the window's frame, either here or
-     * in finishLayout().
+     * Called when the state of allow-lockscreen-when-on of the display is changed. See
+     * {@link WindowManager.LayoutParams#FLAG_ALLOW_LOCK_WHILE_SCREEN_ON}
      *
-     * @param win The window being positioned.
-     * @param attached For sub-windows, the window it is attached to; this
-     *                 window will already have had layoutWindow() called on it
-     *                 so you can use its Rect.  Otherwise null.
-     * @param displayFrames The display frames.
+     * @param displayId The ID of the display.
+     * @param allow Whether the display allows showing lockscreen when it is on.
      */
-    default void layoutWindowLw(
-            WindowState win, WindowState attached, DisplayFrames displayFrames) {}
-
-    /**
-     * Return the layout hints for a newly added window. These values are computed on the
-     * most recent layout, so they are not guaranteed to be correct.
-     *
-     * @param attrs The LayoutParams of the window.
-     * @param taskBounds The bounds of the task this window is on or {@code null} if no task is
-     *                   associated with the window.
-     * @param displayFrames display frames.
-     * @param floatingStack Whether the window's stack is floating.
-     * @param outFrame The frame of the window.
-     * @param outContentInsets The areas covered by system windows, expressed as positive insets.
-     * @param outStableInsets The areas covered by stable system windows irrespective of their
-     *                        current visibility. Expressed as positive insets.
-     * @param outOutsets The areas that are not real display, but we would like to treat as such.
-     * @param outDisplayCutout The area that has been cut away from the display.
-     * @return Whether to always consume the navigation bar.
-     *         See {@link #isNavBarForcedShownLw(WindowState)}.
-     */
-    default boolean getLayoutHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
-            DisplayFrames displayFrames, boolean floatingStack,
-            Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
-            DisplayCutout.ParcelableWrapper outDisplayCutout) {
-        return false;
-    }
-
-    /**
-     * Called following layout of all windows before each window has policy applied.
-     *
-     * @param displayWidth The current full width of the screen.
-     * @param displayHeight The current full height of the screen.
-     */
-    public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight);
-
-    /**
-     * Called following layout of all window to apply policy to each window.
-     *
-     * @param win The window being positioned.
-     * @param attrs The LayoutParams of the window.
-     * @param attached For sub-windows, the window it is attached to. Otherwise null.
-     */
-    public void applyPostLayoutPolicyLw(WindowState win,
-            WindowManager.LayoutParams attrs, WindowState attached, WindowState imeTarget);
-
-    /**
-     * Called following layout of all windows and after policy has been applied
-     * to each window. If in this function you do
-     * something that may have modified the animation state of another window,
-     * be sure to return non-zero in order to perform another pass through layout.
-     *
-     * @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT},
-     * {@link #FINISH_LAYOUT_REDO_CONFIG}, {@link #FINISH_LAYOUT_REDO_WALLPAPER},
-     * or {@link #FINISH_LAYOUT_REDO_ANIM}.
-     */
-    public int finishPostLayoutPolicyLw();
-
-    /**
-     * Return true if it is okay to perform animations for an app transition
-     * that is about to occur. You may return false for this if, for example,
-     * the dream window is currently displayed so the switch should happen
-     * immediately.
-     */
-    public boolean allowAppAnimationsLw();
-
-    /**
-     * A new window has been focused.
-     */
-    public int focusChangedLw(WindowState lastFocus, WindowState newFocus);
+    void setAllowLockscreenWhenOn(int displayId, boolean allow);
 
     /**
      * Called when the device has started waking up.
@@ -1430,8 +1237,6 @@
      */
     public boolean isKeyguardDrawnLw();
 
-    public boolean isShowingDreamLw();
-
     /**
      * Called when the system is mostly done booting to set whether
      * the system should go into safe mode.
@@ -1491,14 +1296,6 @@
     public void keepScreenOnStoppedLw();
 
     /**
-     * Called when a new system UI visibility is being reported, allowing
-     * the policy to adjust what is actually reported.
-     * @param visibility The raw visibility reported by the status bar.
-     * @return The new desired visibility.
-     */
-    public int adjustSystemUiVisibilityLw(int visibility);
-
-    /**
      * Called by System UI to notify of changes to the visibility of Recents.
      */
     public void setRecentsVisibilityLw(boolean visible);
@@ -1548,6 +1345,16 @@
     public void showGlobalActions();
 
     /**
+     * Returns whether the user setup is complete.
+     */
+    boolean isUserSetupComplete();
+
+    /**
+     * Returns the current UI mode.
+     */
+    int getUiMode();
+
+    /**
      * Called when the current user changes. Guaranteed to be called before the broadcast
      * of the new user id is made to all listeners.
      *
@@ -1602,69 +1409,6 @@
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
 
     /**
-     * Calculates the stable insets without running a layout.
-     *
-     * @param displayRotation the current display rotation
-     * @param displayWidth the current display width
-     * @param displayHeight the current display height
-     * @param displayCutout the current display cutout
-     * @param outInsets the insets to return
-     */
-    public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
-            DisplayCutout displayCutout, Rect outInsets);
-
-
-    /**
-     * @return true if the navigation bar is forced to stay visible
-     */
-    public boolean isNavBarForcedShownLw(WindowState win);
-
-    /**
-     * @return The side of the screen where navigation bar is positioned.
-     * @see #NAV_BAR_LEFT
-     * @see #NAV_BAR_RIGHT
-     * @see #NAV_BAR_BOTTOM
-     */
-    @NavigationBarPosition
-    int getNavBarPosition();
-
-    /**
-     * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
-     * bar or button bar. See {@link #getNonDecorDisplayWidth}.
-     *
-     * @param displayRotation the current display rotation
-     * @param displayWidth the current display width
-     * @param displayHeight the current display height
-     * @param displayCutout the current display cutout
-     * @param outInsets the insets to return
-     */
-    public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
-            DisplayCutout displayCutout, Rect outInsets);
-
-    /**
-     * @param displayRotation the current display rotation
-     * @param displayWidth the current display width
-     * @param displayHeight the current display height
-     * @param dockSide the dockside asking if allowed
-     * @param originalDockSide the side that was original docked to in split screen
-     * @return True if a specified {@param dockSide} is allowed on the current device, or false
-     *         otherwise. It is guaranteed that at least one dock side for a particular orientation
-     *         is allowed, so for example, if DOCKED_RIGHT is not allowed, DOCKED_LEFT is allowed.
-     *         If navigation bar is movable then the docked side would bias towards the
-     *         {@param originalDockSide}.
-     */
-    public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
-            int displayHeight, int displayRotation);
-
-    /**
-     * Called when the configuration has changed, and it's safe to load new values from resources.
-     */
-    public void onConfigurationChanged(DisplayContentInfo displayContentInfo);
-
-    public boolean shouldRotateSeamlessly(DisplayRotation displayRotation,
-            int oldRotation, int newRotation);
-
-    /**
      * Called when System UI has been started.
      */
     void onSystemUiStarted();
@@ -1697,17 +1441,6 @@
     public void requestUserActivityNotification();
 
     /**
-     * Called when the state of lock task mode changes. This should be used to disable immersive
-     * mode confirmation.
-     *
-     * @param lockTaskState the new lock task mode state. One of
-     *                      {@link ActivityManager#LOCK_TASK_MODE_NONE},
-     *                      {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
-     *                      {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
-     */
-    void onLockTaskStateChangedLw(int lockTaskState);
-
-    /**
      * Updates the flag about whether AOD is showing.
      *
      * @return whether the value was changed.
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 812fd82..79e2688 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.power;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.thermal.V1_0.ThermalStatus;
 import android.hardware.thermal.V1_0.ThermalStatusCode;
@@ -26,12 +27,16 @@
 import android.os.HwBinder;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
+import android.os.IThermalStatusListener;
 import android.os.PowerManager;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.Temperature;
+import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
@@ -39,6 +44,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.NoSuchElementException;
 
@@ -51,210 +57,154 @@
 public class ThermalManagerService extends SystemService {
     private static final String TAG = ThermalManagerService.class.getSimpleName();
 
-    /** Registered observers of the thermal changed events. Cookie is used to store type */
+    /** Lock to protect listen list. */
+    private final Object mLock = new Object();
+
+    /**
+     * Registered observers of the thermal events. Cookie is used to store type as Integer, null
+     * means no filter.
+     */
     @GuardedBy("mLock")
     private final RemoteCallbackList<IThermalEventListener> mThermalEventListeners =
             new RemoteCallbackList<>();
 
-    /** Lock to protect HAL handles and listen list. */
-    private final Object mLock = new Object();
-
-    /** Newly registered callback. */
+    /** Registered observers of the thermal status. */
     @GuardedBy("mLock")
-    private IThermalEventListener mNewListenerCallback = null;
+    private final RemoteCallbackList<IThermalStatusListener> mThermalStatusListeners =
+            new RemoteCallbackList<>();
 
-    /** Newly registered callback type, null means not filter type. */
+    /** Current thermal status */
     @GuardedBy("mLock")
-    private Integer mNewListenerType = null;
+    private int mStatus;
+
+    /** Current thermal map, key as name */
+    @GuardedBy("mLock")
+    private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
 
     /** Local PMS handle. */
     private final PowerManager mPowerManager;
 
-    /** Proxy object for the Thermal HAL 2.0 service. */
+    /** HAL wrapper. */
+    private ThermalHalWrapper mHalWrapper;
+
+    /** Hal ready. */
     @GuardedBy("mLock")
-    private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
+    private boolean mHalReady;
 
-    /** Proxy object for the Thermal HAL 1.1 service. */
-    @GuardedBy("mLock")
-    private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
-
-    /** Cookie for matching the right end point. */
-    private static final int THERMAL_HAL_DEATH_COOKIE = 5612;
-
-    /** HWbinder callback for Thermal HAL 2.0. */
-    private final IThermalChangedCallback.Stub mThermalCallback20 =
-            new IThermalChangedCallback.Stub() {
-                @Override
-                public void notifyThrottling(
-                        android.hardware.thermal.V2_0.Temperature temperature) {
-                    android.os.Temperature thermalSvcTemp = new android.os.Temperature(
-                            temperature.value, temperature.type, temperature.name,
-                            temperature.throttlingStatus);
-                    final long token = Binder.clearCallingIdentity();
-                    try {
-                        notifyThrottlingImpl(thermalSvcTemp);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
-                }
-            };
-
-    /** HWbinder callback for Thermal HAL 1.1. */
-    private final IThermalCallback.Stub mThermalCallback11 =
-            new IThermalCallback.Stub() {
-                @Override
-                public void notifyThrottling(boolean isThrottling,
-                        android.hardware.thermal.V1_0.Temperature temperature) {
-                    android.os.Temperature thermalSvcTemp = new android.os.Temperature(
-                            temperature.currentValue, temperature.type, temperature.name,
-                            isThrottling ? ThrottlingSeverity.SEVERE : ThrottlingSeverity.NONE);
-                    final long token = Binder.clearCallingIdentity();
-                    try {
-                        notifyThrottlingImpl(thermalSvcTemp);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
-                }
-            };
+    /** Invalid throttling status */
+    private static final int INVALID_THROTTLING = Integer.MIN_VALUE;
 
     public ThermalManagerService(Context context) {
+        this(context, null);
+    }
+
+    @VisibleForTesting
+    ThermalManagerService(Context context, @Nullable ThermalHalWrapper halWrapper) {
         super(context);
         mPowerManager = context.getSystemService(PowerManager.class);
+        mHalWrapper = halWrapper;
+        // Initialize to invalid to send status onActivityManagerReady
+        mStatus = INVALID_THROTTLING;
     }
 
-    private void setNewListener(IThermalEventListener listener, Integer type) {
-        synchronized (mLock) {
-            mNewListenerCallback = listener;
-            mNewListenerType = type;
+    @Override
+    public void onStart() {
+        publishBinderService(Context.THERMAL_SERVICE, mService);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            onActivityManagerReady();
         }
     }
 
-    private void clearNewListener() {
+    private void onActivityManagerReady() {
         synchronized (mLock) {
-            mNewListenerCallback = null;
-            mNewListenerType = null;
-        }
-    }
-
-    private final IThermalService.Stub mService = new IThermalService.Stub() {
-        @Override
-        public void registerThermalEventListener(IThermalEventListener listener) {
-            synchronized (mLock) {
-                mThermalEventListeners.register(listener, null);
-                // Notify its callback after new client registered.
-                setNewListener(listener, null);
-                long token = Binder.clearCallingIdentity();
-                try {
-                    notifyCurrentTemperaturesLocked();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    clearNewListener();
+            // Connect to HAL and post to listeners.
+            boolean halConnected = (mHalWrapper != null);
+            if (!halConnected) {
+                mHalWrapper = new ThermalHal20Wrapper();
+                halConnected = mHalWrapper.connectToHal();
+                if (!halConnected) {
+                    mHalWrapper = new ThermalHal11Wrapper();
+                    halConnected = mHalWrapper.connectToHal();
                 }
             }
-        }
-
-        @Override
-        public void registerThermalEventListenerWithType(IThermalEventListener listener, int type) {
-            synchronized (mLock) {
-                mThermalEventListeners.register(listener, new Integer(type));
-                setNewListener(listener, new Integer(type));
-                // Notify its callback after new client registered.
-                long token = Binder.clearCallingIdentity();
-                try {
-                    notifyCurrentTemperaturesLocked();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    clearNewListener();
-                }
+            mHalWrapper.setCallback(this::onTemperatureChangedCallback);
+            if (!halConnected) {
+                return;
             }
-        }
-
-        @Override
-        public void unregisterThermalEventListener(IThermalEventListener listener) {
-            synchronized (mLock) {
-                long token = Binder.clearCallingIdentity();
-                try {
-                    mThermalEventListeners.unregister(listener);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
+            List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(false,
+                    0);
+            final int count = temperatures.size();
+            for (int i = 0; i < count; i++) {
+                onTemperatureChanged(temperatures.get(i), false);
             }
+            onTemperatureMapChangedLocked();
+            mHalReady = halConnected /* true */;
         }
-
-        @Override
-        public List<android.os.Temperature> getCurrentTemperatures() {
-            List<android.os.Temperature> ret;
-            long token = Binder.clearCallingIdentity();
-            try {
-                ret = getCurrentTemperaturesInternal(false, 0 /* not used */);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            return ret;
-        }
-
-        @Override
-        public List<android.os.Temperature> getCurrentTemperaturesWithType(int type) {
-            List<android.os.Temperature> ret;
-            long token = Binder.clearCallingIdentity();
-            try {
-                ret = getCurrentTemperaturesInternal(true, type);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            return ret;
-        }
-
-        @Override
-        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
-            pw.println("ThermalEventListeners dump:");
-            synchronized (mLock) {
-                mThermalEventListeners.dump(pw, "\t");
-                pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes" : "no"));
-                pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes" : "no"));
-            }
-        }
-    };
-
-    private List<android.os.Temperature> getCurrentTemperaturesInternal(boolean shouldFilter,
-            int type) {
-        List<android.os.Temperature> ret = new ArrayList<>();
-        synchronized (mLock) {
-            if (mThermalHal20 == null) {
-                return ret;
-            }
-            try {
-                mThermalHal20.getCurrentTemperatures(shouldFilter, type,
-                        (ThermalStatus status,
-                                ArrayList<android.hardware.thermal.V2_0.Temperature>
-                                        temperatures) -> {
-                            if (ThermalStatusCode.SUCCESS == status.code) {
-                                for (android.hardware.thermal.V2_0.Temperature
-                                        temperature : temperatures) {
-                                    ret.add(new android.os.Temperature(
-                                            temperature.value, temperature.type, temperature.name,
-                                            temperature.throttlingStatus));
-                                }
-                            } else {
-                                Slog.e(TAG,
-                                        "Couldn't get temperatures because of HAL error: "
-                                                + status.debugMessage);
-                            }
-
-                        });
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
-                connectToHalLocked();
-                // Post to listeners after reconnect to HAL.
-                notifyCurrentTemperaturesLocked();
-            }
-        }
-        return ret;
     }
 
-    private void notifyListener(android.os.Temperature temperature, IThermalEventListener listener,
-            Integer type) {
+    private void postStatusListener(IThermalStatusListener listener) {
+        final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
+            try {
+                listener.onStatusChange(mStatus);
+            } catch (RemoteException | RuntimeException e) {
+                Slog.e(TAG, "Thermal callback failed to call", e);
+            }
+        });
+        if (!thermalCallbackQueued) {
+            Slog.e(TAG, "Thermal callback failed to queue");
+        }
+    }
+
+    private void notifyStatusListenersLocked() {
+        if (!Temperature.isValidStatus(mStatus)) {
+            return;
+        }
+        final int length = mThermalStatusListeners.beginBroadcast();
+        try {
+            for (int i = 0; i < length; i++) {
+                final IThermalStatusListener listener =
+                        mThermalStatusListeners.getBroadcastItem(i);
+                postStatusListener(listener);
+            }
+        } finally {
+            mThermalStatusListeners.finishBroadcast();
+        }
+    }
+
+    private void onTemperatureMapChangedLocked() {
+        int newStatus = INVALID_THROTTLING;
+        final int count = mTemperatureMap.size();
+        for (int i = 0; i < count; i++) {
+            Temperature t = mTemperatureMap.valueAt(i);
+            if (t.getStatus() >= newStatus) {
+                newStatus = t.getStatus();
+            }
+        }
+        if (newStatus != mStatus) {
+            mStatus = newStatus;
+            notifyStatusListenersLocked();
+        }
+    }
+
+
+    private void postEventListenerCurrentTemperatures(IThermalEventListener listener,
+            @Nullable Integer type) {
+        synchronized (mLock) {
+            final int count = mTemperatureMap.size();
+            for (int i = 0; i < count; i++) {
+                postEventListener(mTemperatureMap.valueAt(i), listener,
+                        type);
+            }
+        }
+    }
+
+    private void postEventListener(Temperature temperature,
+            IThermalEventListener listener,
+            @Nullable Integer type) {
         // Skip if listener registered with a different type
         if (type != null && type != temperature.getType()) {
             return;
@@ -271,11 +221,26 @@
         }
     }
 
-    private void notifyThrottlingImpl(android.os.Temperature temperature) {
+    private void notifyEventListenersLocked(Temperature temperature) {
+        final int length = mThermalEventListeners.beginBroadcast();
+        try {
+            for (int i = 0; i < length; i++) {
+                final IThermalEventListener listener =
+                        mThermalEventListeners.getBroadcastItem(i);
+                final Integer type =
+                        (Integer) mThermalEventListeners.getBroadcastCookie(i);
+                postEventListener(temperature, listener, type);
+            }
+        } finally {
+            mThermalEventListeners.finishBroadcast();
+        }
+    }
+
+    private void onTemperatureChanged(Temperature temperature, boolean sendStatus) {
         synchronized (mLock) {
             // Thermal Shutdown for Skin temperature
-            if (temperature.getStatus() == android.os.Temperature.THROTTLING_SHUTDOWN
-                    && temperature.getType() == android.os.Temperature.TYPE_SKIN) {
+            if (temperature.getStatus() == Temperature.THROTTLING_SHUTDOWN
+                    && temperature.getType() == Temperature.TYPE_SKIN) {
                 final long token = Binder.clearCallingIdentity();
                 try {
                     mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
@@ -284,107 +249,420 @@
                 }
             }
 
-            if (mNewListenerCallback != null) {
-                // Only notify current newly added callback.
-                notifyListener(temperature, mNewListenerCallback, mNewListenerType);
+            Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
+            if (old != null) {
+                if (old.getStatus() != temperature.getStatus()) {
+                    notifyEventListenersLocked(temperature);
+                }
             } else {
-                final int length = mThermalEventListeners.beginBroadcast();
-                try {
-                    for (int i = 0; i < length; i++) {
-                        final IThermalEventListener listener =
-                                mThermalEventListeners.getBroadcastItem(i);
-                        final Integer type = (Integer) mThermalEventListeners.getBroadcastCookie(i);
-                        notifyListener(temperature, listener, type);
-                    }
-                } finally {
-                    mThermalEventListeners.finishBroadcast();
-                }
+                notifyEventListenersLocked(temperature);
+            }
+            if (sendStatus) {
+                onTemperatureMapChangedLocked();
             }
         }
     }
 
-    @Override
-    public void onStart() {
-        publishBinderService(Context.THERMAL_SERVICE, mService);
+    private void onTemperatureChangedCallback(Temperature temperature) {
+        onTemperatureChanged(temperature, true);
     }
 
-    @Override
-    public void onBootPhase(int phase) {
-        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
-            onActivityManagerReady();
+    private void dumpTemperaturesLocked(PrintWriter pw, String prefix,
+            Collection<Temperature> temperatures) {
+        for (Temperature t : temperatures) {
+            pw.print(prefix);
+            String out = String.format("Name: %s, Type: %d, Status: %d, Value: %f",
+                    t.getName(),
+                    t.getType(),
+                    t.getStatus(),
+                    t.getValue()
+            );
+            pw.println(out);
         }
     }
 
-    private void notifyCurrentTemperaturesCallbackLocked(ThermalStatus status,
-            ArrayList<android.hardware.thermal.V2_0.Temperature> temperatures) {
-        if (ThermalStatusCode.SUCCESS != status.code) {
-            Slog.e(TAG, "Couldn't get temperatures because of HAL error: "
-                    + status.debugMessage);
-            return;
-        }
-        for (android.hardware.thermal.V2_0.Temperature temperature : temperatures) {
-            android.os.Temperature thermal_svc_temp =
-                    new android.os.Temperature(
-                            temperature.value, temperature.type,
-                            temperature.name,
-                            temperature.throttlingStatus);
-            notifyThrottlingImpl(thermal_svc_temp);
-        }
-    }
-
-    private void notifyCurrentTemperaturesLocked() {
-        if (mThermalHal20 == null) {
-            return;
-        }
-        try {
-            mThermalHal20.getCurrentTemperatures(false, 0,
-                    this::notifyCurrentTemperaturesCallbackLocked);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Couldn't get temperatures, reconnecting...", e);
-            connectToHalLocked();
-        }
-    }
-
-    private void onActivityManagerReady() {
-        synchronized (mLock) {
-            connectToHalLocked();
-            // Post to listeners after connect to HAL.
-            notifyCurrentTemperaturesLocked();
-        }
-    }
-
-    final class DeathRecipient implements HwBinder.DeathRecipient {
+    @VisibleForTesting
+    final IThermalService.Stub mService = new IThermalService.Stub() {
         @Override
-        public void serviceDied(long cookie) {
-            if (cookie == THERMAL_HAL_DEATH_COOKIE) {
-                Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
+        public boolean registerThermalEventListener(IThermalEventListener listener) {
+            synchronized (mLock) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    if (!mThermalEventListeners.register(listener, null)) {
+                        return false;
+                    }
+                    if (mHalReady) {
+                        // Notify its callback after new client registered.
+                        postEventListenerCurrentTemperatures(listener, null);
+                    }
+                    return true;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public boolean registerThermalEventListenerWithType(IThermalEventListener listener,
+                int type) {
+            synchronized (mLock) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    if (!mThermalEventListeners.register(listener, new Integer(type))) {
+                        return false;
+                    }
+                    if (mHalReady) {
+                        // Notify its callback after new client registered.
+                        postEventListenerCurrentTemperatures(listener, new Integer(type));
+                    }
+                    return true;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public boolean unregisterThermalEventListener(IThermalEventListener listener) {
+            synchronized (mLock) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    return mThermalEventListeners.unregister(listener);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public List<Temperature> getCurrentTemperatures() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!mHalReady) {
+                    return new ArrayList<>();
+                }
+                return mHalWrapper.getCurrentTemperatures(false, 0 /* not used */);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public List<Temperature> getCurrentTemperaturesWithType(int type) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!mHalReady) {
+                    return new ArrayList<>();
+                }
+                return mHalWrapper.getCurrentTemperatures(true, type);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public boolean registerThermalStatusListener(IThermalStatusListener listener) {
+            synchronized (mLock) {
+                // Notify its callback after new client registered.
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    if (!mThermalStatusListeners.register(listener)) {
+                        return false;
+                    }
+                    if (mHalReady) {
+                        // Notify its callback after new client registered.
+                        postStatusListener(listener);
+                    }
+                    return true;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public boolean unregisterThermalStatusListener(IThermalStatusListener listener) {
+            synchronized (mLock) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    return mThermalStatusListeners.unregister(listener);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public int getCurrentStatus() {
+            synchronized (mLock) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    return Temperature.isValidStatus(mStatus) ? mStatus
+                            : Temperature.THROTTLING_NONE;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
                 synchronized (mLock) {
-                    connectToHalLocked();
-                    // Post to listeners after reconnect to HAL.
-                    notifyCurrentTemperaturesLocked();
+                    pw.println("ThermalEventListeners:");
+                    mThermalEventListeners.dump(pw, "\t");
+                    pw.println("ThermalStatusListeners:");
+                    mThermalStatusListeners.dump(pw, "\t");
+                    pw.println("Thermal Status: " + Integer.toString(mStatus));
+                    pw.println("Cached temperatures:");
+                    dumpTemperaturesLocked(pw, "\t", mTemperatureMap.values());
+                    pw.println("HAL Ready: " + Boolean.toString(mHalReady));
+                    if (mHalReady) {
+                        pw.println("HAL connection:");
+                        mHalWrapper.dump(pw, "\t");
+                        pw.println("Current temperatures from HAL:");
+                        dumpTemperaturesLocked(pw, "\t",
+                                mHalWrapper.getCurrentTemperatures(false, 0));
+                    }
+                }
+
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    abstract static class ThermalHalWrapper {
+        protected static final String TAG = ThermalHalWrapper.class.getSimpleName();
+
+        /** Lock to protect HAL handle. */
+        protected final Object mHalLock = new Object();
+
+        @FunctionalInterface
+        interface TemperatureChangedCallback {
+            void onValues(Temperature temperature);
+        }
+
+        /** Temperature callback. */
+        protected TemperatureChangedCallback mCallback;
+
+        /** Cookie for matching the right end point. */
+        protected static final int THERMAL_HAL_DEATH_COOKIE = 5612;
+
+        @VisibleForTesting
+        protected void setCallback(TemperatureChangedCallback cb) {
+            mCallback = cb;
+        }
+
+        protected abstract List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+                int type);
+
+        protected abstract boolean connectToHal();
+
+        protected abstract void dump(PrintWriter pw, String prefix);
+
+        protected void resendCurrentTemperatures() {
+            synchronized (mHalLock) {
+                List<Temperature> temperatures = getCurrentTemperatures(false, 0);
+                final int count = temperatures.size();
+                for (int i = 0; i < count; i++) {
+                    mCallback.onValues(temperatures.get(i));
+                }
+            }
+        }
+
+        final class DeathRecipient implements HwBinder.DeathRecipient {
+            @Override
+            public void serviceDied(long cookie) {
+                if (cookie == THERMAL_HAL_DEATH_COOKIE) {
+                    Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
+                    synchronized (mHalLock) {
+                        connectToHal();
+                        // Post to listeners after reconnect to HAL.
+                        resendCurrentTemperatures();
+                    }
                 }
             }
         }
     }
 
-    private void connectToHalLocked() {
-        try {
-            mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
-            mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
-            mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
-                    0 /* not used */);
-        } catch (NoSuchElementException | RemoteException e) {
-            Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1.");
-            mThermalHal20 = null;
-            try {
-                mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
-                mThermalHal11.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
-                mThermalHal11.registerThermalCallback(mThermalCallback11);
-            } catch (NoSuchElementException | RemoteException e2) {
-                Slog.e(TAG,
-                        "Thermal HAL 1.1 service not connected, no thermal call back "
-                                + "will be called.");
-                mThermalHal11 = null;
+    static class ThermalHal11Wrapper extends ThermalHalWrapper {
+        /** Proxy object for the Thermal HAL 1.1 service. */
+        @GuardedBy("mHalLock")
+        private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
+
+        /** HWbinder callback for Thermal HAL 1.1. */
+        private final IThermalCallback.Stub mThermalCallback11 =
+                new IThermalCallback.Stub() {
+                    @Override
+                    public void notifyThrottling(boolean isThrottling,
+                            android.hardware.thermal.V1_0.Temperature temperature) {
+                        Temperature thermalSvcTemp = new Temperature(
+                                temperature.currentValue, temperature.type, temperature.name,
+                                isThrottling ? ThrottlingSeverity.SEVERE
+                                        : ThrottlingSeverity.NONE);
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            mCallback.onValues(thermalSvcTemp);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+                };
+
+        @Override
+        protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+                int type) {
+            synchronized (mHalLock) {
+                List<Temperature> ret = new ArrayList<>();
+                if (mThermalHal11 == null) {
+                    return ret;
+                }
+                try {
+                    mThermalHal11.getTemperatures(
+                            (ThermalStatus status,
+                                    ArrayList<android.hardware.thermal.V1_0.Temperature>
+                                            temperatures) -> {
+                                if (ThermalStatusCode.SUCCESS == status.code) {
+                                    for (android.hardware.thermal.V1_0.Temperature
+                                            temperature : temperatures) {
+                                        if (shouldFilter && type != temperature.type) {
+                                            continue;
+                                        }
+                                        // Thermal HAL 1.1 doesn't report current throttling status
+                                        ret.add(new Temperature(
+                                                temperature.currentValue, temperature.type,
+                                                temperature.name,
+                                                Temperature.THROTTLING_NONE));
+                                    }
+                                } else {
+                                    Slog.e(TAG,
+                                            "Couldn't get temperatures because of HAL error: "
+                                                    + status.debugMessage);
+                                }
+
+                            });
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
+                    connectToHal();
+                }
+                return ret;
+            }
+        }
+
+        @Override
+        protected boolean connectToHal() {
+            synchronized (mHalLock) {
+                try {
+                    mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
+                    mThermalHal11.linkToDeath(new DeathRecipient(),
+                            THERMAL_HAL_DEATH_COOKIE);
+                    mThermalHal11.registerThermalCallback(mThermalCallback11);
+                } catch (NoSuchElementException | RemoteException e) {
+                    Slog.e(TAG,
+                            "Thermal HAL 1.1 service not connected, no thermal call back will be "
+                                    + "called.");
+                    mThermalHal11 = null;
+                }
+                return (mThermalHal11 != null);
+            }
+        }
+
+        @Override
+        protected void dump(PrintWriter pw, String prefix) {
+            synchronized (mHalLock) {
+                pw.print(prefix);
+                pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes"
+                        : "no"));
+            }
+        }
+    }
+
+    static class ThermalHal20Wrapper extends ThermalHalWrapper {
+        /** Proxy object for the Thermal HAL 2.0 service. */
+        @GuardedBy("mHalLock")
+        private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
+
+        /** HWbinder callback for Thermal HAL 2.0. */
+        private final IThermalChangedCallback.Stub mThermalCallback20 =
+                new IThermalChangedCallback.Stub() {
+                    @Override
+                    public void notifyThrottling(
+                            android.hardware.thermal.V2_0.Temperature temperature) {
+                        Temperature thermalSvcTemp = new Temperature(
+                                temperature.value, temperature.type, temperature.name,
+                                temperature.throttlingStatus);
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            mCallback.onValues(thermalSvcTemp);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+                };
+
+        @Override
+        protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+                int type) {
+            synchronized (mHalLock) {
+                List<Temperature> ret = new ArrayList<>();
+                if (mThermalHal20 == null) {
+                    return ret;
+                }
+                try {
+                    mThermalHal20.getCurrentTemperatures(shouldFilter, type,
+                            (ThermalStatus status,
+                                    ArrayList<android.hardware.thermal.V2_0.Temperature>
+                                            temperatures) -> {
+                                if (ThermalStatusCode.SUCCESS == status.code) {
+                                    for (android.hardware.thermal.V2_0.Temperature
+                                            temperature : temperatures) {
+                                        ret.add(new Temperature(
+                                                temperature.value, temperature.type,
+                                                temperature.name,
+                                                temperature.throttlingStatus));
+                                    }
+                                } else {
+                                    Slog.e(TAG,
+                                            "Couldn't get temperatures because of HAL error: "
+                                                    + status.debugMessage);
+                                }
+
+                            });
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
+                    connectToHal();
+                }
+                return ret;
+            }
+        }
+
+        @Override
+        protected boolean connectToHal() {
+            synchronized (mHalLock) {
+                try {
+                    mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
+                    mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
+                    mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
+                            0 /* not used */);
+                } catch (NoSuchElementException | RemoteException e) {
+                    Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1.");
+                    mThermalHal20 = null;
+                }
+                return (mThermalHal20 != null);
+            }
+        }
+
+        @Override
+        protected void dump(PrintWriter pw, String prefix) {
+            synchronized (mHalLock) {
+                pw.print(prefix);
+                pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes"
+                        : "no"));
             }
         }
     }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index d2ca850..6f6846d 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1630,37 +1630,42 @@
         if (this.mKernelCpuThreadReader == null) {
             return;
         }
-        KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = this.mKernelCpuThreadReader
-                .getCurrentProcessCpuUsage();
-        if (processCpuUsage == null) {
+        ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
+                this.mKernelCpuThreadReader.getProcessCpuUsageByUids();
+        if (processCpuUsages == null) {
             return;
         }
         int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
-        for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage
-                : processCpuUsage.threadCpuUsages) {
-            if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
-                Slog.w(TAG, "Unexpected number of usage times,"
-                        + " expected " + cpuFrequencies.length
-                        + " but got " + threadCpuUsage.usageTimesMillis.length);
-                continue;
-            }
-
-            for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) {
-                // Do not report CPU usage at a frequency when it's zero
-                if (threadCpuUsage.usageTimesMillis[i] == 0) {
+        for (int i = 0; i < processCpuUsages.size(); i++) {
+            KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
+            ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
+                    processCpuUsage.threadCpuUsages;
+            for (int j = 0; j < threadCpuUsages.size(); j++) {
+                KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j);
+                if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
+                    Slog.w(TAG, "Unexpected number of usage times,"
+                            + " expected " + cpuFrequencies.length
+                            + " but got " + threadCpuUsage.usageTimesMillis.length);
                     continue;
                 }
 
-                StatsLogEventWrapper e =
-                        new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-                e.writeInt(processCpuUsage.uid);
-                e.writeInt(processCpuUsage.processId);
-                e.writeInt(threadCpuUsage.threadId);
-                e.writeString(processCpuUsage.processName);
-                e.writeString(threadCpuUsage.threadName);
-                e.writeInt(cpuFrequencies[i]);
-                e.writeInt(threadCpuUsage.usageTimesMillis[i]);
-                pulledData.add(e);
+                for (int k = 0; k < threadCpuUsage.usageTimesMillis.length; k++) {
+                    // Do not report CPU usage at a frequency when it's zero
+                    if (threadCpuUsage.usageTimesMillis[k] == 0) {
+                        continue;
+                    }
+
+                    StatsLogEventWrapper e =
+                            new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+                    e.writeInt(processCpuUsage.uid);
+                    e.writeInt(processCpuUsage.processId);
+                    e.writeInt(threadCpuUsage.threadId);
+                    e.writeString(processCpuUsage.processName);
+                    e.writeString(threadCpuUsage.threadName);
+                    e.writeInt(cpuFrequencies[k]);
+                    e.writeInt(threadCpuUsage.usageTimesMillis[k]);
+                    pulledData.add(e);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 095eaa5..a66f0ca 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -44,36 +44,42 @@
      */
     void showPictureInPictureMenu();
 
-    void setWindowState(int window, int state);
+    void setWindowState(int displayId, int window, int state);
 
     /**
      * Notifies the status bar that an app transition is pending to delay applying some flags with
      * visual impact until {@link #appTransitionReady} is called.
+     *
+     * @param displayId the ID of the display which has this event.
      */
-    void appTransitionPending();
+    void appTransitionPending(int displayId);
 
     /**
      * Notifies the status bar that a pending app transition has been cancelled.
+     *
+     * @param displayId the ID of the display which has this event.
      */
-    void appTransitionCancelled();
+    void appTransitionCancelled(int displayId);
 
     /**
      * Notifies the status bar that an app transition is now being executed.
      *
+     * @param displayId the ID of the display which has this event.
      * @param statusBarAnimationsStartTime the desired start time for all visual animations in the
      *        status bar caused by this app transition in uptime millis
      * @param statusBarAnimationsDuration the duration for all visual animations in the status
      *        bar caused by this app transition in millis
      */
-    void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration);
+    void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
+            long statusBarAnimationsDuration);
 
     void startAssist(Bundle args);
     void onCameraLaunchGestureDetected(int source);
-    void topAppWindowChanged(boolean menuVisible);
-    void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
-            Rect fullscreenBounds, Rect dockedBounds, String cause);
+    void topAppWindowChanged(int displayId, boolean menuVisible);
+    void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
+            int mask, Rect fullscreenBounds, Rect dockedBounds, String cause);
     void toggleSplitScreen();
-    void appTransitionFinished();
+    void appTransitionFinished(int displayId);
 
     void toggleRecentApps();
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3e07ebe..1eb44a0 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -39,6 +39,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
+import android.view.Display;
 
 import com.android.internal.R;
 import com.android.internal.statusbar.IStatusBar;
@@ -237,14 +238,22 @@
         }
 
         @Override
-        public void topAppWindowChanged(boolean menuVisible) {
+        public void topAppWindowChanged(int displayId, boolean menuVisible) {
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
+                return;
+            }
             StatusBarManagerService.this.topAppWindowChanged(menuVisible);
         }
 
         @Override
-        public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
-                int mask,
-                Rect fullscreenBounds, Rect dockedBounds, String cause) {
+        public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+                int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds,
+                String cause) {
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
+                return;
+            }
             StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
                     dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
         }
@@ -259,8 +268,13 @@
             }
         }
 
-        public void appTransitionFinished() {
+        @Override
+        public void appTransitionFinished(int displayId) {
             enforceStatusBarService();
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
+                return;
+            }
             if (mBar != null) {
                 try {
                     mBar.appTransitionFinished();
@@ -358,7 +372,11 @@
         }
 
         @Override
-        public void setWindowState(int window, int state) {
+        public void setWindowState(int displayId, int window, int state) {
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
+                return;
+            }
             if (mBar != null) {
                 try {
                     mBar.setWindowState(window, state);
@@ -367,7 +385,11 @@
         }
 
         @Override
-        public void appTransitionPending() {
+        public void appTransitionPending(int displayId) {
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
+                return;
+            }
             if (mBar != null) {
                 try {
                     mBar.appTransitionPending();
@@ -376,7 +398,11 @@
         }
 
         @Override
-        public void appTransitionCancelled() {
+        public void appTransitionCancelled(int displayId) {
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
+                return;
+            }
             if (mBar != null) {
                 try {
                     mBar.appTransitionCancelled();
@@ -385,8 +411,12 @@
         }
 
         @Override
-        public void appTransitionStarting(long statusBarAnimationsStartTime,
+        public void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
                 long statusBarAnimationsDuration) {
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
+                return;
+            }
             if (mBar != null) {
                 try {
                     mBar.appTransitionStarting(
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 5ce8145..8d27d1e 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -28,18 +28,22 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.service.textclassifier.IConversationActionsCallback;
 import android.service.textclassifier.ITextClassificationCallback;
 import android.service.textclassifier.ITextClassifierService;
+import android.service.textclassifier.ITextLanguageCallback;
 import android.service.textclassifier.ITextLinksCallback;
 import android.service.textclassifier.ITextSelectionCallback;
 import android.service.textclassifier.TextClassifierService;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassificationSessionId;
+import android.view.textclassifier.TextLanguage;
 import android.view.textclassifier.TextLinks;
 import android.view.textclassifier.TextSelection;
 
@@ -210,6 +214,50 @@
     }
 
     @Override
+    public void onDetectLanguage(
+            TextClassificationSessionId sessionId,
+            TextLanguage.Request request,
+            ITextLanguageCallback callback) throws RemoteException {
+        Preconditions.checkNotNull(request);
+        Preconditions.checkNotNull(callback);
+
+        synchronized (mLock) {
+            UserState userState = getCallingUserStateLocked();
+            if (!userState.bindLocked()) {
+                callback.onFailure();
+            } else if (userState.isBoundLocked()) {
+                userState.mService.onDetectLanguage(sessionId, request, callback);
+            } else {
+                userState.mPendingRequests.add(new PendingRequest(
+                        () -> onDetectLanguage(sessionId, request, callback),
+                        callback::onFailure, callback.asBinder(), this, userState));
+            }
+        }
+    }
+
+    @Override
+    public void onSuggestConversationActions(
+            TextClassificationSessionId sessionId,
+            ConversationActions.Request request,
+            IConversationActionsCallback callback) throws RemoteException {
+        Preconditions.checkNotNull(request);
+        Preconditions.checkNotNull(callback);
+
+        synchronized (mLock) {
+            UserState userState = getCallingUserStateLocked();
+            if (!userState.bindLocked()) {
+                callback.onFailure();
+            } else if (userState.isBoundLocked()) {
+                userState.mService.onSuggestConversationActions(sessionId, request, callback);
+            } else {
+                userState.mPendingRequests.add(new PendingRequest(
+                        () -> onSuggestConversationActions(sessionId, request, callback),
+                        callback::onFailure, callback.asBinder(), this, userState));
+            }
+        }
+    }
+
+    @Override
     public void onCreateTextClassificationSession(
             TextClassificationContext classificationContext, TextClassificationSessionId sessionId)
             throws RemoteException {
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index c4d2851..da0a794 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -48,7 +48,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import libcore.icu.ICU;
 import libcore.timezone.TzDataSetVersion;
-import libcore.util.TimeZoneFinder;
+import libcore.timezone.TimeZoneFinder;
 import libcore.util.ZoneInfoDB;
 
 import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 479f427..409d2b4 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.app.WallpaperManager.FLAG_LOCK;
 import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
 import static android.os.ParcelFileDescriptor.MODE_CREATE;
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
@@ -56,6 +57,7 @@
 import android.graphics.BitmapRegionDecoder;
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -118,6 +120,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 public class WallpaperManagerService extends IWallpaperManager.Stub
         implements IWallpaperManagerService {
@@ -640,6 +643,43 @@
     final IPackageManager mIPackageManager;
     final MyPackageMonitor mMonitor;
     final AppOpsManager mAppOpsManager;
+
+    private final DisplayManager mDisplayManager;
+    private final DisplayManager.DisplayListener mDisplayListener =
+            new DisplayManager.DisplayListener() {
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+            synchronized (mLock) {
+                if (mLastWallpaper != null) {
+                    final WallpaperConnection.DisplayConnector connector =
+                            mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+                    if (connector == null) return;
+
+                    connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            synchronized (mLock) {
+                if (mLastWallpaper != null) {
+                    final WallpaperConnection.DisplayConnector connector =
+                            mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+                    if (connector == null) return;
+                    connector.disconnectLocked();
+                    mLastWallpaper.connection.removeDisplayConnector(displayId);
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            // TODO(b/115486823) Review that do we need to handle display changes.
+        }
+    };
+
     /**
      * Map of color listeners per user id.
      * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
@@ -775,20 +815,96 @@
     class WallpaperConnection extends IWallpaperConnection.Stub
             implements ServiceConnection {
 
+        /**
+         * Collect needed info for a display.
+         */
+        private final class DisplayConnector {
+            final int mDisplayId;
+            final Binder mToken = new Binder();
+            IWallpaperEngine mEngine;
+            boolean mDimensionsChanged;
+            boolean mPaddingChanged;
+
+            DisplayConnector(int displayId) {
+                mDisplayId = displayId;
+            }
+
+            void ensureStatusHandled() {
+                if (mDimensionsChanged) {
+                    try {
+                        mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to set wallpaper dimensions", e);
+                    }
+                    mDimensionsChanged = false;
+                }
+                if (mPaddingChanged) {
+                    try {
+                        mEngine.setDisplayPadding(mWallpaper.padding);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to set wallpaper padding", e);
+                    }
+                    mPaddingChanged = false;
+                }
+            }
+
+            void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
+                if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
+                try {
+                    mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed add wallpaper window token on display " + mDisplayId, e);
+                    return;
+                }
+
+                try {
+                    // TODO(b/115486823) Consider the size of non-default display
+                    connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
+                            wallpaper.width, wallpaper.height,
+                            wallpaper.padding, mDisplayId);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed attaching wallpaper on display", e);
+                    // TODO(b/115486823) Failed when attaching a new engine, however, other engines
+                    // may still working. Should we abandon them all or just ignore this one.
+                    if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating) {
+                        bindWallpaperComponentLocked(null /* componentName */, false /* force */,
+                                false /* fromUser */, wallpaper, null /* reply */);
+                    }
+                }
+            }
+
+            void disconnectLocked() {
+                if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
+                try {
+                    mIWindowManager.removeWindowToken(mToken, mDisplayId);
+                } catch (RemoteException e) {
+                }
+                try {
+                    if (mEngine != null) {
+                        mEngine.destroy();
+                    }
+                } catch (RemoteException e) {
+                }
+                mEngine = null;
+            }
+        }
+
+        /**
+         * A map for each display.
+         * Use {@link #getDisplayConnectorOrCreate(int displayId)} to ensure the display is usable.
+         */
+        private SparseArray<DisplayConnector> mDisplayConnector = new SparseArray<>();
+
         /** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the
          *  middle of an update). If exceeded, the wallpaper gets reset to the system default. */
         private static final long WALLPAPER_RECONNECT_TIMEOUT_MS = 10000;
 
         final WallpaperInfo mInfo;
-        final Binder mToken = new Binder();
         IWallpaperService mService;
-        IWallpaperEngine mEngine;
         WallpaperData mWallpaper;
+        final int mClientUid;
         IRemoteCallback mReply;
 
-        boolean mDimensionsChanged = false;
-        boolean mPaddingChanged = false;
-
         private Runnable mResetRunnable = () -> {
             synchronized (mLock) {
                 if (mShuttingDown) {
@@ -809,9 +925,55 @@
             }
         };
 
-        public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
+        WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
             mInfo = info;
             mWallpaper = wallpaper;
+            mClientUid = clientUid;
+            initDisplayState();
+        }
+
+        private void initDisplayState() {
+            final Display[] displays = mDisplayManager.getDisplays();
+            for (Display display : displays) {
+                if (isUsableDisplay(display)) {
+                    final int displayId = display.getDisplayId();
+                    mDisplayConnector.append(displayId, new DisplayConnector(displayId));
+                }
+            }
+        }
+
+        // TODO(b/115486823) Support the system decorations change at runtime.
+        private boolean isUsableDisplay(Display display) {
+            return display != null &&  display.hasAccess(mClientUid)
+                    // TODO(b/114338689) Use WindowManager.supportsSystemDecorations when ready
+                    && (display.supportsSystemDecorations()
+                            || display.getDisplayId() == DEFAULT_DISPLAY);
+        }
+
+        void forEachDisplayConnector(Consumer<DisplayConnector> action) {
+            for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
+                final DisplayConnector connector = mDisplayConnector.get(i);
+                action.accept(connector);
+            }
+        }
+
+        DisplayConnector getDisplayConnectorOrCreate(int displayId) {
+            DisplayConnector connector = mDisplayConnector.get(displayId);
+            if (connector == null) {
+                final Display display = mDisplayManager.getDisplay(displayId);
+                if (isUsableDisplay(display)) {
+                    connector = new DisplayConnector(displayId);
+                    mDisplayConnector.append(displayId, connector);
+                }
+            }
+            return connector;
+        }
+
+        void removeDisplayConnector(int displayId) {
+            final DisplayConnector connector = mDisplayConnector.get(displayId);
+            if (connector != null) {
+                mDisplayConnector.remove(displayId);
+            }
         }
 
         @Override
@@ -839,7 +1001,7 @@
                             + mWallpaper.wallpaperComponent);
                 }
                 mService = null;
-                mEngine = null;
+                forEachDisplayConnector(connector -> connector.mEngine = null);
                 if (mWallpaper.connection == this) {
                     // There is an inherent ordering race between this callback and the
                     // package monitor that receives notice that a package is being updated,
@@ -863,7 +1025,8 @@
             fgHandler.removeCallbacks(mResetRunnable);
             fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
             if (DEBUG_LIVE) {
-                Slog.i(TAG, "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
+                Slog.i(TAG,
+                        "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
             }
         }
 
@@ -943,38 +1106,37 @@
         }
 
         @Override
-        public void attachEngine(IWallpaperEngine engine) {
+        public void attachEngine(IWallpaperEngine engine, int displayId) {
             synchronized (mLock) {
-                mEngine = engine;
-                if (mDimensionsChanged) {
+                final DisplayConnector connector = getDisplayConnectorOrCreate(displayId);
+                if (connector == null) {
                     try {
-                        mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+                        engine.destroy();
                     } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to set wallpaper dimensions", e);
+                        Slog.w(TAG, "Failed to destroy engine", e);
                     }
-                    mDimensionsChanged = false;
+                    return;
                 }
-                if (mPaddingChanged) {
+                connector.mEngine = engine;
+                connector.ensureStatusHandled();
+
+                // TODO(multi-display) TBD.
+                if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) {
                     try {
-                        mEngine.setDisplayPadding(mWallpaper.padding);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to set wallpaper padding", e);
-                    }
-                    mPaddingChanged = false;
-                }
-                if (mInfo != null && mInfo.supportsAmbientMode()) {
-                    try {
-                        mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
+                        connector.mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to set ambient mode state", e);
                     }
                 }
-                try {
-                    // This will trigger onComputeColors in the wallpaper engine.
-                    // It's fine to be locked in here since the binder is oneway.
-                    mEngine.requestWallpaperColors();
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to request wallpaper colors", e);
+                // TODO(b/115486823) Extends for secondary display.
+                if (displayId == DEFAULT_DISPLAY) {
+                    try {
+                        // This will trigger onComputeColors in the wallpaper engine.
+                        // It's fine to be locked in here since the binder is oneway.
+                        connector.mEngine.requestWallpaperColors();
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to request wallpaper colors", e);
+                    }
                 }
             }
         }
@@ -1162,6 +1324,8 @@
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mIPackageManager = AppGlobals.getPackageManager();
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mDisplayManager = mContext.getSystemService(DisplayManager.class);
+        mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
         mMonitor = new MyPackageMonitor();
         mColorsChangedListeners = new SparseArray<>();
     }
@@ -1541,6 +1705,7 @@
         return false;
     }
 
+    // TODO(b/115486823) Extends this method with specific display.
     public void setDimensionHints(int width, int height, String callingPackage)
             throws RemoteException {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
@@ -1560,10 +1725,12 @@
                 saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
-                    if (wallpaper.connection.mEngine != null) {
+                    // TODO(b/115486823) Extends this method with specific display.
+                    final IWallpaperEngine engine = wallpaper.connection
+                            .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+                    if (engine != null) {
                         try {
-                            wallpaper.connection.mEngine.setDesiredSize(
-                                    width, height);
+                            engine.setDesiredSize(width, height);
                         } catch (RemoteException e) {
                         }
                         notifyCallbacksLocked(wallpaper);
@@ -1571,7 +1738,9 @@
                         // We've attached to the service but the engine hasn't attached back to us
                         // yet. This means it will be created with the previous dimensions, so we
                         // need to update it to the new dimensions once it attaches.
-                        wallpaper.connection.mDimensionsChanged = true;
+                        // TODO(b/115486823) Extends this method with specific display.
+                        wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
+                                .mDimensionsChanged = true;
                     }
                 }
             }
@@ -1600,6 +1769,7 @@
         }
     }
 
+    // TODO(b/115486823) Extends this method with specific display.
     public void setDisplayPadding(Rect padding, String callingPackage) {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
         if (!isWallpaperSupported(callingPackage)) {
@@ -1617,9 +1787,12 @@
                 saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
-                    if (wallpaper.connection.mEngine != null) {
+                    // TODO(b/115486823) Extends this method with specific display.
+                    final IWallpaperEngine engine = wallpaper.connection
+                            .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+                    if (engine != null) {
                         try {
-                            wallpaper.connection.mEngine.setDisplayPadding(padding);
+                            engine.setDisplayPadding(padding);
                         } catch (RemoteException e) {
                         }
                         notifyCallbacksLocked(wallpaper);
@@ -1627,7 +1800,9 @@
                         // We've attached to the service but the engine hasn't attached back to us
                         // yet. This means it will be created with the previous dimensions, so we
                         // need to update it to the new dimensions once it attaches.
-                        wallpaper.connection.mPaddingChanged = true;
+                        // TODO(b/115486823) Extends this method with specific display.
+                        wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
+                                .mPaddingChanged = true;
                     }
                 }
             }
@@ -1756,6 +1931,7 @@
         }
     }
 
+    // TODO(b/115486823) Extends this method with specific display.
     public void setInAmbientMode(boolean inAmbienMode, boolean animated) {
         final IWallpaperEngine engine;
         synchronized (mLock) {
@@ -1763,7 +1939,8 @@
             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
             if (data != null && data.connection != null && data.connection.mInfo != null
                     && data.connection.mInfo.supportsAmbientMode()) {
-                engine = data.connection.mEngine;
+                // TODO(b/115486823) Extends this method with specific display.
+                engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
             } else {
                 engine = null;
             }
@@ -2125,7 +2302,9 @@
 
             // Bind the service!
             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
-            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
+            final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
+                    MATCH_DIRECT_BOOT_AUTO, wallpaper.userId);
+            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid);
             intent.setComponent(componentName);
             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                     com.android.internal.R.string.wallpaper_binding_label);
@@ -2152,14 +2331,8 @@
             wallpaper.wallpaperComponent = componentName;
             wallpaper.connection = newConn;
             newConn.mReply = reply;
-            try {
-                if (wallpaper.userId == mCurrentUserId) {
-                    if (DEBUG)
-                        Slog.v(TAG, "Adding window token: " + newConn.mToken);
-                    mIWindowManager.addWindowToken(newConn.mToken, TYPE_WALLPAPER, DEFAULT_DISPLAY);
-                    mLastWallpaper = wallpaper;
-                }
-            } catch (RemoteException e) {
+            if (wallpaper.userId == mCurrentUserId) {
+                mLastWallpaper = wallpaper;
             }
         } catch (RemoteException e) {
             String msg = "Remote exception for " + componentName + "\n" + e;
@@ -2181,22 +2354,12 @@
                 }
                 wallpaper.connection.mReply = null;
             }
-            if (wallpaper.connection.mEngine != null) {
-                try {
-                    wallpaper.connection.mEngine.destroy();
-                } catch (RemoteException e) {
-                }
-            }
             mContext.unbindService(wallpaper.connection);
-            try {
-                if (DEBUG)
-                    Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
-                mIWindowManager.removeWindowToken(wallpaper.connection.mToken, DEFAULT_DISPLAY);
-            } catch (RemoteException e) {
-            }
+            wallpaper.connection.forEachDisplayConnector(connector -> connector.disconnectLocked());
             wallpaper.connection.mService = null;
-            wallpaper.connection.mEngine = null;
+            wallpaper.connection.mDisplayConnector.clear();
             wallpaper.connection = null;
+            if (wallpaper == mLastWallpaper) mLastWallpaper = null;
         }
     }
 
@@ -2206,16 +2369,7 @@
     }
 
     void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
-        try {
-            conn.mService.attach(conn, conn.mToken,
-                    TYPE_WALLPAPER, false,
-                    wallpaper.width, wallpaper.height, wallpaper.padding);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
-            if (!wallpaper.wallpaperUpdating) {
-                bindWallpaperComponentLocked(null, false, false, wallpaper, null);
-            }
-        }
+        conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
     }
 
     private void notifyCallbacksLocked(WallpaperData wallpaper) {
@@ -2801,12 +2955,16 @@
                         pw.print("    mInfo.component=");
                         pw.println(conn.mInfo.getComponent());
                     }
-                    pw.print("    mToken=");
-                    pw.println(conn.mToken);
+                    conn.forEachDisplayConnector(connector -> {
+                        pw.print("    mDisplayId=");
+                        pw.println(connector.mDisplayId);
+                        pw.print("    mToken=");
+                        pw.println(connector.mToken);
+                        pw.print("    mEngine=");
+                        pw.println(connector.mEngine);
+                    });
                     pw.print("    mService=");
                     pw.println(conn.mService);
-                    pw.print("    mEngine=");
-                    pw.println(conn.mEngine);
                     pw.print("    mLastDiedTime=");
                     pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c43e64e..5e92b9e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2354,7 +2354,7 @@
         // bounds would end up too small.
         outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top);
 
-        if (service.mWindowManager.getNavBarPosition() == NAV_BAR_LEFT) {
+        if (service.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) {
             // Position the activity frame on the opposite side of the nav bar.
             outBounds.left = appBounds.right - maxActivityWidth;
             outBounds.right = appBounds.right;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 8d467c8..bd3e43c 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2548,11 +2548,16 @@
             // If the current top activity may be able to occlude keyguard but the occluded state
             // has not been set, update visibility and check again if we should continue to resume.
             boolean nothingToResume = true;
-            if (!mService.mShuttingDown && !mTopActivityOccludesKeyguard
-                    && next.canShowWhenLocked()) {
-                ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
-                        !PRESERVE_WINDOWS);
-                nothingToResume = shouldSleepActivities();
+            if (!mService.mShuttingDown) {
+                final boolean canShowWhenLocked = !mTopActivityOccludesKeyguard
+                        && next.canShowWhenLocked();
+                final boolean mayDismissKeyguard = mTopDismissingKeyguardActivity != next
+                        && next.hasDismissKeyguardWindows();
+                if (canShowWhenLocked || mayDismissKeyguard) {
+                    ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+                            !PRESERVE_WINDOWS);
+                    nothingToResume = shouldSleepActivities();
+                }
             }
             if (nothingToResume) {
                 // Make sure we have executed any pending transitions, since there
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 94a47dd..32a6f74 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -51,7 +51,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
 
-import android.app.WindowConfiguration;
 import android.os.Trace;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -83,7 +82,7 @@
     AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
         mDisplayContent = displayContent;
-        mWallpaperControllerLocked = new WallpaperController(mService);
+        mWallpaperControllerLocked = mDisplayContent.mWallpaperController;
     }
 
     /**
@@ -97,16 +96,17 @@
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
 
         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
-        int transit = mDisplayContent.mAppTransition.getAppTransition();
+        final AppTransition appTransition = mDisplayContent.mAppTransition;
+        int transit = appTransition.getAppTransition();
         if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
             transit = WindowManager.TRANSIT_UNSET;
         }
         mDisplayContent.mSkipAppTransitionAnimation = false;
         mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
 
-        mDisplayContent.mAppTransition.removeAppTransitionTimeoutCallbacks();
+        appTransition.removeAppTransitionTimeoutCallbacks();
 
-        mService.mRoot.mWallpaperMayChange = false;
+        mDisplayContent.mWallpaperMayChange = false;
 
         int i;
         for (i = 0; i < appsCount; i++) {
@@ -121,7 +121,7 @@
         // Adjust wallpaper before we pull the lower/upper target, since pending changes
         // (like the clearAnimatingFlags() above) might affect wallpaper target result.
         // Or, the opening app window should be a wallpaper target.
-        mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(mDisplayContent,
+        mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
                 mDisplayContent.mOpeningApps);
 
         // Determine if closing and opening app token sets are wallpaper targets, in which case
@@ -142,7 +142,7 @@
         // done behind a dream window.
         final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
                 mDisplayContent.mClosingApps);
-        final boolean allowAnimations = mService.mPolicy.allowAppAnimationsLw();
+        final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw();
         final AppWindowToken animLpToken = allowAnimations
                 ? findAnimLayoutParamsToken(transit, activityTypes)
                 : null;
@@ -166,15 +166,15 @@
             handleClosingApps(transit, animLp, voiceInteraction);
             handleOpeningApps(transit, animLp, voiceInteraction);
 
-            mDisplayContent.mAppTransition.setLastAppTransition(transit, topOpeningApp,
+            appTransition.setLastAppTransition(transit, topOpeningApp,
                     topClosingApp);
 
-            final int flags = mDisplayContent.mAppTransition.getTransitFlags();
-            layoutRedo = mDisplayContent.mAppTransition.goodToGo(transit, topOpeningApp,
+            final int flags = appTransition.getTransitFlags();
+            layoutRedo = appTransition.goodToGo(transit, topOpeningApp,
                     topClosingApp, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps);
             handleNonAppWindowsInTransition(transit, flags);
-            mDisplayContent.mAppTransition.postAnimationCallback();
-            mDisplayContent.mAppTransition.clear();
+            appTransition.postAnimationCallback();
+            appTransition.clear();
         } finally {
             mService.mSurfaceAnimationRunner.continueStartingAnimations();
         }
@@ -255,8 +255,8 @@
     }
 
     /**
-     * @return The set of {@link WindowConfiguration.ActivityType}s contained in the set of apps in
-     *         {@code array1} and {@code array2}.
+     * @return The set of {@link android.app.WindowConfiguration.ActivityType}s contained in the set
+     *         of apps in {@code array1} and {@code array2}.
      */
     private static ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1,
             ArraySet<AppWindowToken> array2) {
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/wm/BarController.java
similarity index 90%
rename from services/core/java/com/android/server/policy/BarController.java
rename to services/core/java/com/android/server/wm/BarController.java
index 14c985c..a335fa2 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.policy;
+package com.android.server.wm;
 
 import static com.android.server.wm.BarControllerProto.STATE;
 import static com.android.server.wm.BarControllerProto.TRANSIENT_STATE;
@@ -30,7 +30,7 @@
 import android.view.WindowManager;
 
 import com.android.server.LocalServices;
-import com.android.server.policy.WindowManagerPolicy.WindowState;
+import com.android.server.UiThread;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.PrintWriter;
@@ -59,7 +59,7 @@
     private final int mTranslucentWmFlag;
     protected final Handler mHandler;
     private final Object mServiceAquireLock = new Object();
-    protected StatusBarManagerInternal mStatusBarInternal;
+    private StatusBarManagerInternal mStatusBarInternal;
 
     protected WindowState mWin;
     private int mState = StatusBarManager.WINDOW_STATE_SHOWING;
@@ -73,7 +73,7 @@
 
     private OnBarVisibilityChangedListener mVisibilityChangeListener;
 
-    public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag,
+    BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag,
             int statusBarManagerId, int translucentWmFlag, int transparentFlag) {
         mTag = "BarController." + tag;
         mTransientFlag = transientFlag;
@@ -85,7 +85,7 @@
         mHandler = new BarHandler();
     }
 
-    public void setWindow(WindowState win) {
+    void setWindow(WindowState win) {
         mWin = win;
     }
 
@@ -94,11 +94,11 @@
      *
      * This is used to determine if letterboxes interfere with the display of such content.
      */
-    public void setContentFrame(Rect frame) {
+    void setContentFrame(Rect frame) {
         mContentFrame.set(frame);
     }
 
-    public void setShowTransparent(boolean transparent) {
+    void setShowTransparent(boolean transparent) {
         if (transparent != mShowTransparent) {
             mShowTransparent = transparent;
             mSetUnHideFlagWhenNextTransparent = transparent;
@@ -106,27 +106,27 @@
         }
     }
 
-    public void showTransient() {
+    void showTransient() {
         if (mWin != null) {
             setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
         }
     }
 
-    public boolean isTransientShowing() {
+    boolean isTransientShowing() {
         return mTransientBarState == TRANSIENT_BAR_SHOWING;
     }
 
-    public boolean isTransientShowRequested() {
+    boolean isTransientShowRequested() {
         return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED;
     }
 
-    public boolean wasRecentlyTranslucent() {
+    boolean wasRecentlyTranslucent() {
         return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS;
     }
 
-    public void adjustSystemUiVisibilityLw(int oldVis, int vis) {
-        if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING &&
-                (vis & mTransientFlag) == 0) {
+    void adjustSystemUiVisibilityLw(int oldVis, int vis) {
+        if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING
+                && (vis & mTransientFlag) == 0) {
             // sysui requests hide
             setTransientBarState(TRANSIENT_BAR_HIDING);
             setBarShowingLw(false);
@@ -136,7 +136,7 @@
         }
     }
 
-    public int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
+    int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
         if (mWin != null) {
             if (win != null && (win.getAttrs().privateFlags
                     & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) {
@@ -164,7 +164,7 @@
         return win == null || !win.isLetterboxedOverlappingWith(mContentFrame);
     }
 
-    public boolean setBarShowingLw(final boolean show) {
+    boolean setBarShowingLw(final boolean show) {
         if (mWin == null) return false;
         if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
             mPendingShow = true;
@@ -227,7 +227,7 @@
                 public void run() {
                     StatusBarManagerInternal statusbar = getStatusBarInternal();
                     if (statusbar != null) {
-                        statusbar.setWindowState(mStatusBarManagerId, state);
+                        statusbar.setWindowState(mWin.getDisplayId(), mStatusBarManagerId, state);
                     }
                 }
             });
@@ -236,7 +236,7 @@
         return false;
     }
 
-    public boolean checkHiddenLw() {
+    boolean checkHiddenLw() {
         if (mWin != null && mWin.isDrawnLw()) {
             if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) {
                 updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN);
@@ -254,7 +254,7 @@
         return false;
     }
 
-    public boolean checkShowTransientBarLw() {
+    boolean checkShowTransientBarLw() {
         if (mTransientBarState == TRANSIENT_BAR_SHOWING) {
             if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown");
             return false;
@@ -272,7 +272,7 @@
         }
     }
 
-    public int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
+    int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
         if (mWin == null) return vis;
         if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested
             if (transientAllowed) {
@@ -296,8 +296,8 @@
             vis |= mTransientFlag;  // ignore clear requests until transition completes
             vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;  // never show transient bars in low profile
         }
-        if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 ||
-                ((vis | oldVis) & mTransparentFlag) != 0) {
+        if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0
+                || ((vis | oldVis) & mTransparentFlag) != 0) {
             mLastTranslucent = SystemClock.uptimeMillis();
         }
         return vis;
@@ -330,14 +330,14 @@
         throw new IllegalArgumentException("Unknown state " + state);
     }
 
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(STATE, mState);
         proto.write(TRANSIENT_STATE, mTransientBarState);
         proto.end(token);
     }
 
-    public void dump(PrintWriter pw, String prefix) {
+    void dump(PrintWriter pw, String prefix) {
         if (mWin != null) {
             pw.print(prefix); pw.println(mTag);
             pw.print(prefix); pw.print("  "); pw.print("mState"); pw.print('=');
@@ -349,6 +349,10 @@
     }
 
     private class BarHandler extends Handler {
+        BarHandler() {
+            super(UiThread.getHandler().getLooper());
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ab7d259..886b2ff 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -156,6 +156,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
+import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 
@@ -182,9 +183,6 @@
 /**
  * Utility class for keeping track of the WindowStates and other pertinent contents of a
  * particular Display.
- *
- * IMPORTANT: No method from this class should ever be used without holding
- * WindowManagerService.mWindowMap.
  */
 class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
         implements WindowManagerPolicy.DisplayContentInfo {
@@ -289,7 +287,8 @@
      * @see WindowManagerService#createWatermarkInTransaction()
      */
     final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
-    /** @see #computeCompatSmallestWidth(boolean, int, int, int, int) */
+
+    /** @see #computeCompatSmallestWidth(boolean, int, int, int, DisplayCutout) */
     private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
 
     /**
@@ -414,6 +413,8 @@
 
     WallpaperController mWallpaperController;
 
+    boolean mWallpaperMayChange = false;
+
     private final SurfaceSession mSession = new SurfaceSession();
 
     /**
@@ -506,6 +507,11 @@
 
     private final PointerEventDispatcher mPointerEventDispatcher;
 
+    // Last systemUiVisibility we received from status bar.
+    private int mLastStatusBarVisibility = 0;
+    // Last systemUiVisibility we dispatched to windows.
+    private int mLastDispatchedSystemUiVisibility = 0;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final AppWindowToken atoken = w.mAppToken;
@@ -647,7 +653,7 @@
                 w.mLayoutNeeded = false;
                 w.prelayout();
                 final boolean firstLayout = !w.isLaidOut();
-                mService.mPolicy.layoutWindowLw(w, null, mDisplayFrames);
+                getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
                 w.mLayoutSeq = mLayoutSeq;
 
                 // If this is the first layout, we need to initialize the last inset values as
@@ -686,7 +692,7 @@
                 }
                 w.mLayoutNeeded = false;
                 w.prelayout();
-                mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
+                getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
                 w.mLayoutSeq = mLayoutSeq;
                 if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
                         + " mContainingFrame=" + w.getContainingFrame()
@@ -706,7 +712,7 @@
     };
 
     private final Consumer<WindowState> mApplyPostLayoutPolicy =
-            w -> mService.mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow(),
+            w -> getDisplayPolicy().applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow(),
                     mInputMethodTarget);
 
     private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
@@ -751,8 +757,7 @@
             }
         }
 
-        if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
-                && mWallpaperController.isWallpaperTarget(w)) {
+        if (obscuredChanged && w.isVisibleLw() && mWallpaperController.isWallpaperTarget(w)) {
             // This is the wallpaper target and its obscured state changed... make sure the
             // current wallpaper's visibility has been updated accordingly.
             mWallpaperController.updateWallpaperVisibility();
@@ -784,7 +789,7 @@
                 if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
                     if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
                             "First draw done in potential wallpaper target " + w);
-                    root.mWallpaperMayChange = true;
+                    mWallpaperMayChange = true;
                     pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                     if (DEBUG_LAYOUT_REPEATS) {
                         surfacePlacer.debugLayoutRepeats(
@@ -816,11 +821,10 @@
      * initialize direct children.
      * @param display May not be null.
      * @param service You know.
-     * @param wallpaperController wallpaper windows controller used to adjust the positioning of the
-     *                            wallpaper windows in the window list.
+     * @param controller The controller for the display container.
      */
     DisplayContent(Display display, WindowManagerService service,
-            WallpaperController wallpaperController, DisplayWindowController controller) {
+            DisplayWindowController controller) {
         super(service);
         setController(controller);
         if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
@@ -831,22 +835,13 @@
 
         mDisplay = display;
         mDisplayId = display.getDisplayId();
-        mWallpaperController = wallpaperController;
+        mWallpaperController = new WallpaperController(mService, this);
         display.getDisplayInfo(mDisplayInfo);
         display.getMetrics(mDisplayMetrics);
         isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
         mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo,
                 calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
         initializeDisplayBaseInfo();
-        mDisplayPolicy = new DisplayPolicy(service);
-        mDisplayRotation = new DisplayRotation(service, this);
-        if (isDefaultDisplay) {
-            // The policy may be invoked right after here, so it requires the necessary default
-            // fields of this display content.
-            mService.mPolicy.setDefaultDisplay(this);
-        }
-        mDividerControllerLocked = new DockedStackDividerController(service, this);
-        mPinnedStackControllerLocked = new PinnedStackController(service, this);
 
         mAppTransition = new AppTransition(service.mContext, service, this);
         mAppTransition.registerListenerLocked(service.mActivityManagerAppTransitionNotifier);
@@ -857,6 +852,30 @@
         mBoundsAnimationController = new BoundsAnimationController(service.mContext,
                 mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler);
 
+        if (mService.mInputManager != null) {
+            final InputChannel inputChannel = mService.mInputManager.monitorInput("Display "
+                    + mDisplayId, mDisplayId);
+            mPointerEventDispatcher = inputChannel != null
+                    ? new PointerEventDispatcher(inputChannel) : null;
+        } else {
+            mPointerEventDispatcher = null;
+        }
+        mDisplayPolicy = new DisplayPolicy(service, this);
+        mDisplayRotation = new DisplayRotation(service, this);
+        if (isDefaultDisplay) {
+            // The policy may be invoked right after here, so it requires the necessary default
+            // fields of this display content.
+            mService.mPolicy.setDefaultDisplay(this);
+        }
+        if (mService.mDisplayReady) {
+            mDisplayPolicy.onConfigurationChanged();
+        }
+        if (mService.mSystemReady) {
+            mDisplayPolicy.systemReady();
+        }
+        mDividerControllerLocked = new DockedStackDividerController(service, this);
+        mPinnedStackControllerLocked = new PinnedStackController(service, this);
+
         // We use this as our arbitrary surface size for buffer-less parents
         // that don't impose cropping on their children. It may need to be larger
         // than the display size because fullscreen windows can be shifted offscreen
@@ -895,15 +914,6 @@
 
         mService.mAnimator.addDisplayLocked(mDisplayId);
         mInputMonitor = new InputMonitor(service, mDisplayId);
-
-        if (mService.mInputManager != null) {
-            final InputChannel inputChannel = mService.mInputManager.monitorInput("Display "
-                    + mDisplayId, mDisplayId);
-            mPointerEventDispatcher = inputChannel != null
-                    ? new PointerEventDispatcher(inputChannel) : null;
-        } else {
-            mPointerEventDispatcher = null;
-        }
     }
 
     boolean isReady() {
@@ -1223,7 +1233,7 @@
         if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
                 + mDisplayId + " based on lastOrientation=" + lastOrientation
                 + " and oldRotation=" + oldRotation);
-        boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(mDisplayRotation,
+        boolean mayRotateSeamlessly = mDisplayPolicy.shouldRotateSeamlessly(mDisplayRotation,
                 oldRotation, rotation);
 
         if (mayRotateSeamlessly) {
@@ -1286,7 +1296,7 @@
 
         setLayoutNeeded();
         final int[] anim = new int[2];
-        mService.mPolicy.selectRotationAnimationLw(anim);
+        mDisplayPolicy.selectRotationAnimationLw(anim);
 
         if (!rotateSeamlessly) {
             mService.startFreezingDisplayLocked(anim[0], anim[1], this);
@@ -1445,10 +1455,10 @@
         final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(mRotation);
         final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
 
-        final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode,
-                mDisplayId, displayCutout);
-        final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode,
-                mDisplayId, displayCutout);
+        final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode,
+                displayCutout);
+        final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode,
+                displayCutout);
         mDisplayInfo.rotation = mRotation;
         mDisplayInfo.logicalWidth = dw;
         mDisplayInfo.logicalHeight = dh;
@@ -1527,13 +1537,13 @@
 
         final float density = mDisplayMetrics.density;
         config.screenWidthDp =
-                (int)(mService.mPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation,
-                        config.uiMode, mDisplayId, displayInfo.displayCutout) / density);
+                (int)(mDisplayPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation,
+                        config.uiMode, displayInfo.displayCutout) / density);
         config.screenHeightDp =
-                (int)(mService.mPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation,
-                        config.uiMode, mDisplayId, displayInfo.displayCutout) / density);
+                (int)(mDisplayPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation,
+                        config.uiMode, displayInfo.displayCutout) / density);
 
-        mService.mPolicy.getNonDecorInsetsLw(displayInfo.rotation, dw, dh,
+        mDisplayPolicy.getNonDecorInsetsLw(displayInfo.rotation, dw, dh,
                 displayInfo.displayCutout, mTmpRect);
         final int leftInset = mTmpRect.left;
         final int topInset = mTmpRect.top;
@@ -1544,8 +1554,8 @@
         final boolean rotated = (displayInfo.rotation == Surface.ROTATION_90
                 || displayInfo.rotation == Surface.ROTATION_270);
 
-        computeSizeRangesAndScreenLayout(displayInfo, mDisplayId, rotated, config.uiMode, dw, dh,
-                density, config);
+        computeSizeRangesAndScreenLayout(displayInfo, rotated, config.uiMode, dw, dh, density,
+                config);
 
         config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
                 | ((displayInfo.flags & Display.FLAG_ROUND) != 0
@@ -1555,7 +1565,7 @@
         config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
         config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
         config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, config.uiMode, dw,
-                dh, displayInfo.displayCutout, mDisplayId);
+                dh, displayInfo.displayCutout);
         config.densityDpi = displayInfo.logicalDensityDpi;
 
         config.colorMode =
@@ -1624,6 +1634,8 @@
             mService.mH.sendEmptyMessage(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
         }
 
+        mDisplayPolicy.updateConfigurationDependentBehaviors();
+
         // Let the policy update hidden states.
         config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
         config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
@@ -1632,7 +1644,7 @@
     }
 
     private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
-            DisplayCutout displayCutout, int displayId) {
+            DisplayCutout displayCutout) {
         mTmpDisplayMetrics.setTo(mDisplayMetrics);
         final DisplayMetrics tmpDm = mTmpDisplayMetrics;
         final int unrotDw, unrotDh;
@@ -1644,22 +1656,22 @@
             unrotDh = dh;
         }
         int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh,
-                displayCutout, displayId);
+                displayCutout);
         sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw,
-                displayCutout, displayId);
+                displayCutout);
         sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh,
-                displayCutout, displayId);
+                displayCutout);
         sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw,
-                displayCutout, displayId);
+                displayCutout);
         return sw;
     }
 
     private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
-            DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout, int displayId) {
-        dm.noncompatWidthPixels = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
-                displayId, displayCutout);
-        dm.noncompatHeightPixels = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
-                uiMode, displayId, displayCutout);
+            DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout) {
+        dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
+                displayCutout);
+        dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
+                displayCutout);
         float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
         int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
         if (curSize == 0 || size < curSize) {
@@ -1668,8 +1680,8 @@
         return curSize;
     }
 
-    private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, int displayId,
-            boolean rotated, int uiMode, int dw, int dh, float density, Configuration outConfig) {
+    private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, boolean rotated,
+            int uiMode, int dw, int dh, float density, Configuration outConfig) {
 
         // We need to determine the smallest width that will occur under normal
         // operation.  To this, start with the base screen size and compute the
@@ -1687,34 +1699,28 @@
         displayInfo.smallestNominalAppHeight = 1<<30;
         displayInfo.largestNominalAppWidth = 0;
         displayInfo.largestNominalAppHeight = 0;
-        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_0, uiMode, unrotDw,
-                unrotDh);
-        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_90, uiMode, unrotDh,
-                unrotDw);
-        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_180, uiMode, unrotDw,
-                unrotDh);
-        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_270, uiMode, unrotDh,
-                unrotDw);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, uiMode, unrotDw, unrotDh);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, uiMode, unrotDh, unrotDw);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, uiMode, unrotDw, unrotDh);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, uiMode, unrotDh, unrotDw);
         int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
         sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
-                displayInfo.displayCutout, displayId);
+                displayInfo.displayCutout);
         sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode,
-                displayInfo.displayCutout, displayId);
+                displayInfo.displayCutout);
         sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode,
-                displayInfo.displayCutout, displayId);
+                displayInfo.displayCutout);
         sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode,
-                displayInfo.displayCutout, displayId);
+                displayInfo.displayCutout);
         outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
         outConfig.screenLayout = sl;
     }
 
     private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh,
-            int uiMode, DisplayCutout displayCutout, int displayId) {
+            int uiMode, DisplayCutout displayCutout) {
         // Get the app screen size at this rotation.
-        int w = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayId,
-                displayCutout);
-        int h = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayId,
-                displayCutout);
+        int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
+        int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout);
 
         // Compute the screen layout size class for this rotation.
         int longSize = w;
@@ -1729,20 +1735,20 @@
         return Configuration.reduceScreenLayout(curLayout, longSize, shortSize);
     }
 
-    private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int displayId, int rotation,
+    private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation,
             int uiMode, int dw, int dh) {
         final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
                 rotation).getDisplayCutout();
-        final int width = mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode,
-                displayId, displayCutout);
+        final int width = mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode,
+                displayCutout);
         if (width < displayInfo.smallestNominalAppWidth) {
             displayInfo.smallestNominalAppWidth = width;
         }
         if (width > displayInfo.largestNominalAppWidth) {
             displayInfo.largestNominalAppWidth = width;
         }
-        final int height = mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode,
-                displayId, displayCutout);
+        final int height = mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode,
+                displayCutout);
         if (height < displayInfo.smallestNominalAppHeight) {
             displayInfo.smallestNominalAppHeight = height;
         }
@@ -1887,6 +1893,9 @@
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         super.onConfigurationChanged(newParentConfig);
+        if (mDisplayPolicy != null) {
+            mDisplayPolicy.onConfigurationChanged();
+        }
 
         // If there was no pinned stack, we still need to notify the controller of the display info
         // update as a result of the config change.
@@ -2383,7 +2392,7 @@
             mDisplayReady = false;
             mRemovingDisplay = false;
         }
-
+        mDisplayPolicy.onDisplayRemoved();
         mInputMonitor.onRemoved();
         mService.mWindowPlacerLocked.requestTraversal();
     }
@@ -2659,6 +2668,13 @@
             }
         }
         pw.print("  mFocusedApp="); pw.println(mFocusedApp);
+        if (mLastStatusBarVisibility != 0) {
+            pw.print("  mLastStatusBarVisibility=0x");
+            pw.println(Integer.toHexString(mLastStatusBarVisibility));
+        }
+
+        pw.println();
+        mWallpaperController.dump(pw, "  ");
 
         pw.println();
         pw.println(prefix + "Application tokens in top down Z order:");
@@ -2849,9 +2865,7 @@
             }
         }
 
-        // System UI is only shown on the default display.
-        int focusChanged = isDefaultDisplay
-                ? mService.mPolicy.focusChangedLw(oldFocus, newFocus) : 0;
+        int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
 
         if (imWindowChanged && oldFocus != mInputMethodWindow) {
             // Focus of the input method window changed. Perform layout if needed.
@@ -3318,6 +3332,31 @@
         return win != null;
     }
 
+    void statusBarVisibilityChanged(int visibility) {
+        mLastStatusBarVisibility = visibility;
+        visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(visibility);
+        updateStatusBarVisibilityLocked(visibility);
+    }
+
+    private boolean updateStatusBarVisibilityLocked(int visibility) {
+        if (mLastDispatchedSystemUiVisibility == visibility) {
+            return false;
+        }
+        final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility)
+                // We are only interested in differences of one of the
+                // clearable flags...
+                & View.SYSTEM_UI_CLEARABLE_FLAGS
+                // ...if it has actually been cleared.
+                & ~visibility;
+
+        mLastDispatchedSystemUiVisibility = visibility;
+        if (isDefaultDisplay) {
+            mService.mInputManager.setSystemUiVisibility(visibility);
+        }
+        updateSystemUiVisibility(visibility, globalDiff);
+        return true;
+    }
+
     void updateSystemUiVisibility(int visibility, int globalDiff) {
         forAllWindows(w -> {
             try {
@@ -3338,6 +3377,13 @@
         }, true /* traverseTopToBottom */);
     }
 
+    void reevaluateStatusBarVisibility() {
+        int visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
+        if (updateStatusBarVisibilityLocked(visibility)) {
+            mService.mWindowPlacerLocked.requestTraversal();
+        }
+    }
+
     void onWindowFreezeTimeout() {
         Slog.w(TAG_WM, "Window freeze timeout expired.");
         mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
@@ -3369,9 +3415,6 @@
 
     // TODO: Super crazy long method that should be broken down...
     void applySurfaceChangesTransaction(boolean recoveringMemory) {
-
-        final int dw = mDisplayInfo.logicalWidth;
-        final int dh = mDisplayInfo.logicalHeight;
         final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
 
         mTmpUpdateAllDrawn.clear();
@@ -3388,12 +3431,8 @@
             if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("On entry to LockedInner",
                     pendingLayoutChanges);
 
-            // TODO(multi-display): For now adjusting wallpaper only on primary display to avoid
-            // the wallpaper window jumping across displays.
-            // Remove check for default display when there will be support for multiple wallpaper
-            // targets (on different displays).
-            if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
-                mWallpaperController.adjustWallpaperWindows(this);
+            if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+                mWallpaperController.adjustWallpaperWindows();
             }
 
             if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
@@ -3418,13 +3457,11 @@
             // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think it is animating.
             pendingLayoutChanges = 0;
 
-            if (isDefaultDisplay) {
-                mService.mPolicy.beginPostLayoutPolicyLw(dw, dh);
-                forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
-                pendingLayoutChanges |= mService.mPolicy.finishPostLayoutPolicyLw();
-                if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
-                        "after finishPostLayoutPolicyLw", pendingLayoutChanges);
-            }
+            mDisplayPolicy.beginPostLayoutPolicyLw();
+            forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
+            pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
+            if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
+                    "after finishPostLayoutPolicyLw", pendingLayoutChanges);
         } while (pendingLayoutChanges != 0);
 
         mTmpApplySurfaceChangesTransactionState.reset();
@@ -3514,12 +3551,7 @@
         // TODO: Not sure if we really need to set the rotation here since we are updating from the
         // display info above...
         mDisplayFrames.mRotation = mRotation;
-        mService.mPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
-        if (isDefaultDisplay) {
-            // Not needed on non-default displays.
-            mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
-            mService.mScreenRect.set(0, 0, dw, dh);
-        }
+        mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
 
         int seq = mLayoutSeq + 1;
         if (seq < 0) seq = 0;
@@ -4549,7 +4581,7 @@
      * However we need child windows of the applications to be above the IME (Text drag handles).
      * This is a non-strictly hierarcical layering and we need to break out of the Z ordering
      * somehow. We do this by relatively ordering children of the target to the IME in cooperation
-     * with {@link #WindowState#assignLayer}
+     * with {@link WindowState#assignLayer}
      */
     void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) {
         child.assignRelativeLayer(t, mImeWindowsContainers.getSurfaceControl(), 1);
@@ -4673,7 +4705,7 @@
             Slog.v(TAG_WM, "Wallpaper layer changed: assigning layers + relayout");
         }
         computeImeTarget(true /* updateImeTarget */);
-        mService.mRoot.mWallpaperMayChange = true;
+        mWallpaperMayChange = true;
         // Since the window list has been rebuilt, focus might have to be recomputed since the
         // actual order of windows might have changed again.
         mService.mFocusMayChange = true;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 9151ddf..c16f95e 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -16,20 +16,143 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
+import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+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_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
+import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerGlobal.ADD_OKAY;
 import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
 import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
+
+import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_HIDE;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.localLOGV;
 
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.StatusBarManager;
+import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.hardware.input.InputManager;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.PrintWriterPrinter;
 import android.util.Slog;
+import android.view.DisplayCutout;
+import android.view.Gravity;
+import android.view.IApplicationToken;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.MotionEvent;
+import android.view.PointerIcon;
+import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.view.WindowManagerGlobal;
+import android.view.WindowManagerPolicyConstants;
+import android.view.accessibility.AccessibilityManager;
 
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ScreenShapeHelper;
+import com.android.internal.util.ScreenshotHelper;
+import com.android.server.LocalServices;
+import com.android.server.UiThread;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.policy.WindowManagerPolicy.InputConsumer;
+import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition;
 import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
 import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
+import com.android.server.policy.WindowOrientationListener;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.utils.InsetUtils;
 
 import java.io.PrintWriter;
 
@@ -38,12 +161,61 @@
  */
 public class DisplayPolicy {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM;
+    private static final boolean DEBUG = false;
+
+    private static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false;
+
+    // The panic gesture may become active only after the keyguard is dismissed and the immersive
+    // app shows again. If that doesn't happen for 30s we drop the gesture.
+    private static final long PANIC_GESTURE_EXPIRATION = 30000;
+
+    // Controls navigation bar opacity depending on which workspace stacks are currently
+    // visible.
+    // Nav bar is always opaque when either the freeform stack or docked stack is visible.
+    private static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0;
+    // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque.
+    private static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 1;
+
+    /**
+     * These are the system UI flags that, when changing, can cause the layout
+     * of the screen to change.
+     */
+    private static final int SYSTEM_UI_CHANGING_LAYOUT =
+            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_FULLSCREEN
+                    | View.STATUS_BAR_TRANSLUCENT
+                    | View.NAVIGATION_BAR_TRANSLUCENT
+                    | View.STATUS_BAR_TRANSPARENT
+                    | View.NAVIGATION_BAR_TRANSPARENT;
 
     private final WindowManagerService mService;
+    private final Context mContext;
+    private final DisplayContent mDisplayContent;
     private final Object mLock;
+    private final Handler mHandler;
 
     private final boolean mCarDockEnablesAccelerometer;
     private final boolean mDeskDockEnablesAccelerometer;
+    private final boolean mTranslucentDecorEnabled;
+    private final AccessibilityManager mAccessibilityManager;
+    private final ImmersiveModeConfirmation mImmersiveModeConfirmation;
+    private final ScreenshotHelper mScreenshotHelper;
+
+    private final Object mServiceAcquireLock = new Object();
+    private StatusBarManagerInternal mStatusBarManagerInternal;
+
+    private StatusBarManagerInternal getStatusBarManagerInternal() {
+        synchronized (mServiceAcquireLock) {
+            if (mStatusBarManagerInternal == null) {
+                mStatusBarManagerInternal =
+                        LocalServices.getService(StatusBarManagerInternal.class);
+            }
+            return mStatusBarManagerInternal;
+        }
+    }
+
+    @VisibleForTesting
+    private final SystemGesturesPointerEventListener mSystemGestures;
 
     private volatile int mLidState = LID_ABSENT;
     private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@@ -64,32 +236,311 @@
     private volatile boolean mKeyguardDrawComplete;
     private volatile boolean mWindowManagerDrawComplete;
 
-    DisplayPolicy(WindowManagerService service) {
+    private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>();
+    private WindowState mStatusBar = null;
+    private final int[] mStatusBarHeightForRotation = new int[4];
+    private WindowState mNavigationBar = null;
+    @NavigationBarPosition
+    private int mNavigationBarPosition = NAV_BAR_BOTTOM;
+    private int[] mNavigationBarHeightForRotationDefault = new int[4];
+    private int[] mNavigationBarWidthForRotationDefault = new int[4];
+    private int[] mNavigationBarHeightForRotationInCarMode = new int[4];
+    private int[] mNavigationBarWidthForRotationInCarMode = new int[4];
+
+    private final StatusBarController mStatusBarController = new StatusBarController();
+
+    private final BarController mNavigationBarController = new BarController("NavigationBar",
+            View.NAVIGATION_BAR_TRANSIENT,
+            View.NAVIGATION_BAR_UNHIDE,
+            View.NAVIGATION_BAR_TRANSLUCENT,
+            StatusBarManager.WINDOW_NAVIGATION_BAR,
+            FLAG_TRANSLUCENT_NAVIGATION,
+            View.NAVIGATION_BAR_TRANSPARENT);
+
+    private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
+            new BarController.OnBarVisibilityChangedListener() {
+                @Override
+                public void onBarVisibilityChanged(boolean visible) {
+                    if (mAccessibilityManager == null) {
+                        return;
+                    }
+                    mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible);
+                }
+            };
+
+    // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+    private NavigationBarExperiments mExperiments = new NavigationBarExperiments();
+    // EXPERIMENT END
+
+    @GuardedBy("mHandler")
+    private SleepToken mDreamingSleepToken;
+
+    @GuardedBy("mHandler")
+    private SleepToken mWindowSleepToken;
+
+    private final Runnable mAcquireSleepTokenRunnable;
+    private final Runnable mReleaseSleepTokenRunnable;
+
+    // The windows we were told about in focusChanged.
+    private WindowState mFocusedWindow;
+    private WindowState mLastFocusedWindow;
+
+    IApplicationToken mFocusedApp;
+
+    int mLastSystemUiFlags;
+    // Bits that we are in the process of clearing, so we want to prevent
+    // them from being set by applications until everything has been updated
+    // to have them clear.
+    private int mResettingSystemUiFlags = 0;
+    // Bits that we are currently always keeping cleared.
+    private int mForceClearedSystemUiFlags = 0;
+    private int mLastFullscreenStackSysUiFlags;
+    private int mLastDockedStackSysUiFlags;
+    private final Rect mNonDockedStackBounds = new Rect();
+    private final Rect mDockedStackBounds = new Rect();
+    private final Rect mLastNonDockedStackBounds = new Rect();
+    private final Rect mLastDockedStackBounds = new Rect();
+
+    // What we last reported to system UI about whether the compatibility
+    // menu needs to be displayed.
+    private boolean mLastFocusNeedsMenu = false;
+    // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
+    private long mPendingPanicGestureUptime;
+
+    private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
+    private static final Rect sTmpRect = new Rect();
+    private static final Rect sTmpDockedFrame = new Rect();
+    private static final Rect sTmpNavFrame = new Rect();
+    private static final Rect sTmpLastParentFrame = new Rect();
+
+    private WindowState mTopFullscreenOpaqueWindowState;
+    private WindowState mTopFullscreenOpaqueOrDimmingWindowState;
+    private WindowState mTopDockedOpaqueWindowState;
+    private WindowState mTopDockedOpaqueOrDimmingWindowState;
+    private boolean mTopIsFullscreen;
+    private boolean mForceStatusBar;
+    private boolean mForceStatusBarFromKeyguard;
+    private boolean mForceStatusBarTransparent;
+    private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
+    private boolean mForcingShowNavBar;
+    private int mForcingShowNavBarLayer;
+    private boolean mForceShowSystemBars;
+
+    private boolean mShowingDream;
+    private boolean mLastShowingDream;
+    private boolean mDreamingLockscreen;
+    private boolean mDreamingSleepTokenNeeded;
+    private boolean mWindowSleepTokenNeeded;
+    private boolean mLastWindowSleepTokenNeeded;
+    private boolean mAllowLockscreenWhenOn;
+
+    private InputConsumer mInputConsumer = null;
+
+    // -------- PolicyHandler --------
+    private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1;
+    private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
+    private static final int MSG_DISPOSE_INPUT_CONSUMER = 3;
+
+    private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
+    private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
+
+    private class PolicyHandler extends Handler {
+
+        PolicyHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_DREAMING_SLEEP_TOKEN:
+                    updateDreamingSleepToken(msg.arg1 != 0);
+                    break;
+                case MSG_REQUEST_TRANSIENT_BARS:
+                    WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
+                            ? mStatusBar : mNavigationBar;
+                    if (targetBar != null) {
+                        requestTransientBars(targetBar);
+                    }
+                    break;
+                case MSG_DISPOSE_INPUT_CONSUMER:
+                    disposeInputConsumer((InputConsumer) msg.obj);
+                    break;
+            }
+        }
+    }
+
+    DisplayPolicy(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
+        mContext = displayContent.isDefaultDisplay ? service.mContext
+                : service.mContext.createDisplayContext(displayContent.getDisplay());
+        mDisplayContent = displayContent;
         mLock = service.getWindowManagerLock();
-        mCarDockEnablesAccelerometer = service.mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_carDockEnablesAccelerometer);
-        mDeskDockEnablesAccelerometer = service.mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_deskDockEnablesAccelerometer);
+
+        final Resources r = mContext.getResources();
+        mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
+        mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer);
+        mTranslucentDecorEnabled = r.getBoolean(R.bool.config_enableTranslucentDecor);
+        updateConfigurationDependentBehaviors();
+
+        mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
+                Context.ACCESSIBILITY_SERVICE);
+        if (!displayContent.isDefaultDisplay) {
+            mAwake = true;
+            mScreenOnEarly = true;
+            mScreenOnFully = true;
+        }
+
+        final Looper looper = UiThread.getHandler().getLooper();
+        mHandler = new PolicyHandler(looper);
+        mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler,
+                new SystemGesturesPointerEventListener.Callbacks() {
+                    @Override
+                    public void onSwipeFromTop() {
+                        if (mStatusBar != null) {
+                            requestTransientBars(mStatusBar);
+                        }
+                    }
+
+                    @Override
+                    public void onSwipeFromBottom() {
+                        if (mNavigationBar != null
+                                && mNavigationBarPosition == NAV_BAR_BOTTOM) {
+                            requestTransientBars(mNavigationBar);
+                        }
+                    }
+
+                    @Override
+                    public void onSwipeFromRight() {
+                        if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) {
+                            requestTransientBars(mNavigationBar);
+                        }
+                    }
+
+                    @Override
+                    public void onSwipeFromLeft() {
+                        if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) {
+                            requestTransientBars(mNavigationBar);
+                        }
+                    }
+
+                    @Override
+                    public void onFling(int duration) {
+                        if (mService.mPowerManagerInternal != null) {
+                            mService.mPowerManagerInternal.powerHint(
+                                    PowerHint.INTERACTION, duration);
+                        }
+                    }
+
+                    @Override
+                    public void onDebug() {
+                        // no-op
+                    }
+
+                    private WindowOrientationListener getOrientationListener() {
+                        final DisplayRotation rotation = mDisplayContent.getDisplayRotation();
+                        return rotation != null ? rotation.getOrientationListener() : null;
+                    }
+
+                    @Override
+                    public void onDown() {
+                        final WindowOrientationListener listener = getOrientationListener();
+                        if (listener != null) {
+                            listener.onTouchStart();
+                        }
+                    }
+
+                    @Override
+                    public void onUpOrCancel() {
+                        final WindowOrientationListener listener = getOrientationListener();
+                        if (listener != null) {
+                            listener.onTouchEnd();
+                        }
+                    }
+
+                    @Override
+                    public void onMouseHoverAtTop() {
+                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
+                        Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
+                        msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS;
+                        mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
+                    }
+
+                    @Override
+                    public void onMouseHoverAtBottom() {
+                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
+                        Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
+                        msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION;
+                        mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
+                    }
+
+                    @Override
+                    public void onMouseLeaveFromEdge() {
+                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
+                    }
+                });
+        displayContent.registerPointerEventListener(mSystemGestures);
+        displayContent.mAppTransition.registerListenerLocked(
+                mStatusBarController.getAppTransitionListener());
+        mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
+                mService.mVrModeEnabled);
+        mAcquireSleepTokenRunnable = () -> {
+            if (mWindowSleepToken != null) {
+                return;
+            }
+            final int displayId = displayContent.getDisplayId();
+            mWindowSleepToken = service.mAtmInternal.acquireSleepToken(
+                    "WindowSleepTokenOnDisplay" + displayId, displayId);
+        };
+        mReleaseSleepTokenRunnable = () -> {
+            if (mWindowSleepToken == null) {
+                return;
+            }
+            mWindowSleepToken.release();
+            mWindowSleepToken = null;
+        };
+
+        // TODO: Make it can take screenshot on external display
+        mScreenshotHelper = displayContent.isDefaultDisplay
+                ? new ScreenshotHelper(mContext) : null;
+    }
+
+    void systemReady() {
+        mSystemGestures.systemReady();
+    }
+
+    private int getDisplayId() {
+        return mDisplayContent.getDisplayId();
+    }
+
+    void onDisplayRemoved() {
+        mDisplayContent.unregisterPointerEventListener(mSystemGestures);
     }
 
     void configure(int width, int height, int shortSizeDp) {
         // Allow the navigation bar to move on non-square small devices (phones).
         mNavigationBarCanMove = width != height && shortSizeDp < 600;
 
-        mHasNavigationBar = mService.mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_showNavigationBar);
+        if (mDisplayContent.isDefaultDisplay) {
+            mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar);
 
-        // Allow a system property to override this. Used by the emulator.
-        // See also hasNavigationBar().
-        String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
-        if ("1".equals(navBarOverride)) {
-            mHasNavigationBar = false;
-        } else if ("0".equals(navBarOverride)) {
-            mHasNavigationBar = true;
+            // Allow a system property to override this. Used by the emulator.
+            // See also hasNavigationBar().
+            String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
+            if ("1".equals(navBarOverride)) {
+                mHasNavigationBar = false;
+            } else if ("0".equals(navBarOverride)) {
+                mHasNavigationBar = true;
+            }
+        } else {
+            mHasNavigationBar = mDisplayContent.getDisplay().supportsSystemDecorations();
         }
     }
 
+    void updateConfigurationDependentBehaviors() {
+        mNavBarOpacityMode = mContext.getResources().getInteger(R.integer.config_navBarOpacityMode);
+    }
+
     public void setHdmiPlugged(boolean plugged) {
         setHdmiPlugged(plugged, false /* force */);
     }
@@ -101,7 +552,7 @@
             final Intent intent = new Intent(ACTION_HDMI_PLUGGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
-            mService.mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
         }
     }
 
@@ -244,17 +695,2642 @@
         return true;
     }
 
+    /**
+     * Sanitize the layout parameters coming from a client.  Allows the policy
+     * to do things like ensure that windows of a specific type can't take
+     * input focus.
+     *
+     * @param attrs The window layout parameters to be modified.  These values
+     * are modified in-place.
+     */
+    public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
+            boolean hasStatusBarServicePermission) {
+
+        final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
+        if (mScreenDecorWindows.contains(win)) {
+            if (!isScreenDecor) {
+                // No longer has the flag set, so remove from the set.
+                mScreenDecorWindows.remove(win);
+            }
+        } else if (isScreenDecor && hasStatusBarServicePermission) {
+            mScreenDecorWindows.add(win);
+        }
+
+        switch (attrs.type) {
+            case TYPE_SYSTEM_OVERLAY:
+            case TYPE_SECURE_SYSTEM_OVERLAY:
+                // These types of windows can't receive input events.
+                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+                attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+                break;
+            case TYPE_DREAM:
+            case TYPE_WALLPAPER:
+                // Dreams and wallpapers don't have an app window token and can thus not be
+                // letterboxed. Hence always let them extend under the cutout.
+                attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+                break;
+            case TYPE_STATUS_BAR:
+
+                // If the Keyguard is in a hidden state (occluded by another window), we force to
+                // remove the wallpaper and keyguard flag so that any change in-flight after setting
+                // the keyguard as occluded wouldn't set these flags again.
+                // See {@link #processKeyguardSetHiddenResultLw}.
+                if (mService.mPolicy.isKeyguardOccluded()) {
+                    attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+                    attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+                }
+                break;
+
+            case TYPE_SCREENSHOT:
+                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+                break;
+
+            case TYPE_TOAST:
+                // While apps should use the dedicated toast APIs to add such windows
+                // it possible legacy apps to add the window directly. Therefore, we
+                // make windows added directly by the app behave as a toast as much
+                // as possible in terms of timeout and animation.
+                if (attrs.hideTimeoutMilliseconds < 0
+                        || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) {
+                    attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
+                }
+                attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
+                break;
+        }
+
+        if (attrs.type != TYPE_STATUS_BAR) {
+            // The status bar is the only window allowed to exhibit keyguard behavior.
+            attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+        }
+    }
+
+    /**
+     * Preflight adding a window to the system.
+     *
+     * Currently enforces that three window types are singletons per display:
+     * <ul>
+     * <li>{@link WindowManager.LayoutParams#TYPE_STATUS_BAR}</li>
+     * <li>{@link WindowManager.LayoutParams#TYPE_NAVIGATION_BAR}</li>
+     * </ul>
+     *
+     * @param win The window to be added
+     * @param attrs Information about the window to be added
+     *
+     * @return If ok, WindowManagerImpl.ADD_OKAY.  If too many singletons,
+     * WindowManagerImpl.ADD_MULTIPLE_SINGLETON
+     */
+    public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) {
+
+        if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.STATUS_BAR_SERVICE,
+                    "DisplayPolicy");
+            mScreenDecorWindows.add(win);
+        }
+
+        switch (attrs.type) {
+            case TYPE_STATUS_BAR:
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.STATUS_BAR_SERVICE,
+                        "DisplayPolicy");
+                if (mStatusBar != null) {
+                    if (mStatusBar.isAlive()) {
+                        return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+                    }
+                }
+                mStatusBar = win;
+                mStatusBarController.setWindow(win);
+                if (mDisplayContent.isDefaultDisplay) {
+                    mService.mPolicy.setKeyguardCandidateLw(win);
+                }
+                break;
+            case TYPE_NAVIGATION_BAR:
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.STATUS_BAR_SERVICE,
+                        "DisplayPolicy");
+                if (mNavigationBar != null) {
+                    if (mNavigationBar.isAlive()) {
+                        return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+                    }
+                }
+                mNavigationBar = win;
+                mNavigationBarController.setWindow(win);
+                mNavigationBarController.setOnBarVisibilityChangedListener(
+                        mNavBarVisibilityListener, true);
+                if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
+                break;
+            case TYPE_NAVIGATION_BAR_PANEL:
+            case TYPE_STATUS_BAR_PANEL:
+            case TYPE_STATUS_BAR_SUB_PANEL:
+            case TYPE_VOICE_INTERACTION_STARTING:
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.STATUS_BAR_SERVICE,
+                        "DisplayPolicy");
+                break;
+        }
+        return ADD_OKAY;
+    }
+
+    /**
+     * Called when a window is being removed from a window manager.  Must not
+     * throw an exception -- clean up as much as possible.
+     *
+     * @param win The window being removed.
+     */
+    public void removeWindowLw(WindowState win) {
+        if (mStatusBar == win) {
+            mStatusBar = null;
+            mStatusBarController.setWindow(null);
+            if (mDisplayContent.isDefaultDisplay) {
+                mService.mPolicy.setKeyguardCandidateLw(null);
+            }
+        } else if (mNavigationBar == win) {
+            mNavigationBar = null;
+            mNavigationBarController.setWindow(null);
+        }
+        if (mLastFocusedWindow == win) {
+            mLastFocusedWindow = null;
+        }
+        mScreenDecorWindows.remove(win);
+    }
+
+    /**
+     * Control the animation to run when a window's state changes.  Return a
+     * non-0 number to force the animation to a specific resource ID, or 0
+     * to use the default animation.
+     *
+     * @param win The window that is changing.
+     * @param transit What is happening to the window:
+     *                {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_ENTER},
+     *                {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_EXIT},
+     *                {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_SHOW}, or
+     *                {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_HIDE}.
+     *
+     * @return Resource ID of the actual animation to use, or 0 for none.
+     */
+    public int selectAnimationLw(WindowState win, int transit) {
+        if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win
+                + ": transit=" + transit);
+        if (win == mStatusBar) {
+            final boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
+            final boolean expanded = win.getAttrs().height == MATCH_PARENT
+                    && win.getAttrs().width == MATCH_PARENT;
+            if (isKeyguard || expanded) {
+                return -1;
+            }
+            if (transit == TRANSIT_EXIT
+                    || transit == TRANSIT_HIDE) {
+                return R.anim.dock_top_exit;
+            } else if (transit == TRANSIT_ENTER
+                    || transit == TRANSIT_SHOW) {
+                return R.anim.dock_top_enter;
+            }
+        } else if (win == mNavigationBar) {
+            if (win.getAttrs().windowAnimations != 0) {
+                return 0;
+            }
+            // This can be on either the bottom or the right or the left.
+            if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
+                if (transit == TRANSIT_EXIT
+                        || transit == TRANSIT_HIDE) {
+                    if (mService.mPolicy.isKeyguardShowingAndNotOccluded()) {
+                        return R.anim.dock_bottom_exit_keyguard;
+                    } else {
+                        return R.anim.dock_bottom_exit;
+                    }
+                } else if (transit == TRANSIT_ENTER
+                        || transit == TRANSIT_SHOW) {
+                    return R.anim.dock_bottom_enter;
+                }
+            } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+                if (transit == TRANSIT_EXIT
+                        || transit == TRANSIT_HIDE) {
+                    return R.anim.dock_right_exit;
+                } else if (transit == TRANSIT_ENTER
+                        || transit == TRANSIT_SHOW) {
+                    return R.anim.dock_right_enter;
+                }
+            } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+                if (transit == TRANSIT_EXIT
+                        || transit == TRANSIT_HIDE) {
+                    return R.anim.dock_left_exit;
+                } else if (transit == TRANSIT_ENTER
+                        || transit == TRANSIT_SHOW) {
+                    return R.anim.dock_left_enter;
+                }
+            }
+        } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
+            return selectDockedDividerAnimationLw(win, transit);
+        }
+
+        if (transit == TRANSIT_PREVIEW_DONE) {
+            if (win.hasAppShownWindows()) {
+                if (DEBUG_ANIM) Slog.i(TAG, "**** STARTING EXIT");
+                return R.anim.app_starting_exit;
+            }
+        } else if (win.getAttrs().type == TYPE_DREAM && mDreamingLockscreen
+                && transit == TRANSIT_ENTER) {
+            // Special case: we are animating in a dream, while the keyguard
+            // is shown.  We don't want an animation on the dream, because
+            // we need it shown immediately with the keyguard animating away
+            // to reveal it.
+            return -1;
+        }
+
+        return 0;
+    }
+
+    private int selectDockedDividerAnimationLw(WindowState win, int transit) {
+        int insets = mDisplayContent.getDockedDividerController().getContentInsets();
+
+        // If the divider is behind the navigation bar, don't animate.
+        final Rect frame = win.getFrameLw();
+        final boolean behindNavBar = mNavigationBar != null
+                && ((mNavigationBarPosition == NAV_BAR_BOTTOM
+                && frame.top + insets >= mNavigationBar.getFrameLw().top)
+                || (mNavigationBarPosition == NAV_BAR_RIGHT
+                && frame.left + insets >= mNavigationBar.getFrameLw().left)
+                || (mNavigationBarPosition == NAV_BAR_LEFT
+                && frame.right - insets <= mNavigationBar.getFrameLw().right));
+        final boolean landscape = frame.height() > frame.width();
+        final boolean offscreenLandscape = landscape && (frame.right - insets <= 0
+                || frame.left + insets >= win.getDisplayFrameLw().right);
+        final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0
+                || frame.bottom + insets >= win.getDisplayFrameLw().bottom);
+        final boolean offscreen = offscreenLandscape || offscreenPortrait;
+        if (behindNavBar || offscreen) {
+            return 0;
+        }
+        if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
+            return R.anim.fade_in;
+        } else if (transit == TRANSIT_EXIT) {
+            return R.anim.fade_out;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Determine the animation to run for a rotation transition based on the
+     * top fullscreen windows {@link WindowManager.LayoutParams#rotationAnimation}
+     * and whether it is currently fullscreen and frontmost.
+     *
+     * @param anim The exiting animation resource id is stored in anim[0], the
+     * entering animation resource id is stored in anim[1].
+     */
+    public void selectRotationAnimationLw(int anim[]) {
+        // If the screen is off or non-interactive, force a jumpcut.
+        final boolean forceJumpcut = !mScreenOnFully || !mService.mPolicy.okToAnimate();
+        if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen="
+                + mTopFullscreenOpaqueWindowState + " rotationAnimation="
+                + (mTopFullscreenOpaqueWindowState == null
+                ? "0" : mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation)
+                + " forceJumpcut=" + forceJumpcut);
+        if (forceJumpcut) {
+            anim[0] = R.anim.rotation_animation_jump_exit;
+            anim[1] = R.anim.rotation_animation_enter;
+            return;
+        }
+        if (mTopFullscreenOpaqueWindowState != null) {
+            int animationHint = mTopFullscreenOpaqueWindowState.getRotationAnimationHint();
+            if (animationHint < 0 && mTopIsFullscreen) {
+                animationHint = mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation;
+            }
+            switch (animationHint) {
+                case ROTATION_ANIMATION_CROSSFADE:
+                case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless.
+                    anim[0] = R.anim.rotation_animation_xfade_exit;
+                    anim[1] = R.anim.rotation_animation_enter;
+                    break;
+                case ROTATION_ANIMATION_JUMPCUT:
+                    anim[0] = R.anim.rotation_animation_jump_exit;
+                    anim[1] = R.anim.rotation_animation_enter;
+                    break;
+                case ROTATION_ANIMATION_ROTATE:
+                default:
+                    anim[0] = anim[1] = 0;
+                    break;
+            }
+        } else {
+            anim[0] = anim[1] = 0;
+        }
+    }
+
+    /**
+     * Validate whether the current top fullscreen has specified the same
+     * {@link WindowManager.LayoutParams#rotationAnimation} value as that
+     * being passed in from the previous top fullscreen window.
+     *
+     * @param exitAnimId exiting resource id from the previous window.
+     * @param enterAnimId entering resource id from the previous window.
+     * @param forceDefault For rotation animations only, if true ignore the
+     * animation values and just return false.
+     * @return true if the previous values are still valid, false if they
+     * should be replaced with the default.
+     */
+    public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId,
+            boolean forceDefault) {
+        switch (exitAnimId) {
+            case R.anim.rotation_animation_xfade_exit:
+            case R.anim.rotation_animation_jump_exit:
+                // These are the only cases that matter.
+                if (forceDefault) {
+                    return false;
+                }
+                int anim[] = new int[2];
+                selectRotationAnimationLw(anim);
+                return (exitAnimId == anim[0] && enterAnimId == anim[1]);
+            default:
+                return true;
+        }
+    }
+
+    /**
+     * Called when a new system UI visibility is being reported, allowing
+     * the policy to adjust what is actually reported.
+     * @param visibility The raw visibility reported by the status bar.
+     * @return The new desired visibility.
+     */
+    public int adjustSystemUiVisibilityLw(int visibility) {
+        mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
+        mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
+
+        // Reset any bits in mForceClearingStatusBarVisibility that
+        // are now clear.
+        mResettingSystemUiFlags &= visibility;
+        // Clear any bits in the new visibility that are currently being
+        // force cleared, before reporting it.
+        return visibility & ~mResettingSystemUiFlags
+                & ~mForceClearedSystemUiFlags;
+    }
+
+    /**
+     * @return true if the navigation bar is forced to stay visible
+     */
+    public boolean isNavBarForcedShownLw(WindowState windowState) {
+        return mForceShowSystemBars;
+    }
+
+    // TODO: Should probably be moved into DisplayFrames.
+    /**
+     * Return the layout hints for a newly added window. These values are computed on the
+     * most recent layout, so they are not guaranteed to be correct.
+     *
+     * @param attrs The LayoutParams of the window.
+     * @param taskBounds The bounds of the task this window is on or {@code null} if no task is
+     *                   associated with the window.
+     * @param displayFrames display frames.
+     * @param floatingStack Whether the window's stack is floating.
+     * @param outFrame The frame of the window.
+     * @param outContentInsets The areas covered by system windows, expressed as positive insets.
+     * @param outStableInsets The areas covered by stable system windows irrespective of their
+     *                        current visibility. Expressed as positive insets.
+     * @param outOutsets The areas that are not real display, but we would like to treat as such.
+     * @param outDisplayCutout The area that has been cut away from the display.
+     * @return Whether to always consume the navigation bar.
+     *         See {@link #isNavBarForcedShownLw(WindowState)}.
+     */
+    public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds,
+            DisplayFrames displayFrames, boolean floatingStack, Rect outFrame,
+            Rect outContentInsets, Rect outStableInsets,
+            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
+        final int fl = PolicyControl.getWindowFlags(null, attrs);
+        final int pfl = attrs.privateFlags;
+        final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
+        final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs);
+        final int displayRotation = displayFrames.mRotation;
+
+        final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
+        if (useOutsets) {
+            int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
+            if (outset > 0) {
+                if (displayRotation == Surface.ROTATION_0) {
+                    outOutsets.bottom += outset;
+                } else if (displayRotation == Surface.ROTATION_90) {
+                    outOutsets.right += outset;
+                } else if (displayRotation == Surface.ROTATION_180) {
+                    outOutsets.top += outset;
+                } else if (displayRotation == Surface.ROTATION_270) {
+                    outOutsets.left += outset;
+                }
+            }
+        }
+
+        final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0;
+        final boolean layoutInScreenAndInsetDecor = layoutInScreen
+                && (fl & FLAG_LAYOUT_INSET_DECOR) != 0;
+        final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
+
+        if (layoutInScreenAndInsetDecor && !screenDecor) {
+            if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
+                outFrame.set(displayFrames.mUnrestricted);
+            } else {
+                outFrame.set(displayFrames.mRestricted);
+            }
+
+            final Rect sf;
+            if (floatingStack) {
+                sf = null;
+            } else {
+                sf = displayFrames.mStable;
+            }
+
+            final Rect cf;
+            if (floatingStack) {
+                cf = null;
+            } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+                if ((fl & FLAG_FULLSCREEN) != 0) {
+                    cf = displayFrames.mStableFullscreen;
+                } else {
+                    cf = displayFrames.mStable;
+                }
+            } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
+                cf = displayFrames.mOverscan;
+            } else {
+                cf = displayFrames.mCurrent;
+            }
+
+            if (taskBounds != null) {
+                outFrame.intersect(taskBounds);
+            }
+            InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets);
+            InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets);
+            outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame)
+                    .getDisplayCutout());
+            return mForceShowSystemBars;
+        } else {
+            if (layoutInScreen) {
+                outFrame.set(displayFrames.mUnrestricted);
+            } else {
+                outFrame.set(displayFrames.mStable);
+            }
+            if (taskBounds != null) {
+                outFrame.intersect(taskBounds);
+            }
+
+            outContentInsets.setEmpty();
+            outStableInsets.setEmpty();
+            outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
+            return mForceShowSystemBars;
+        }
+    }
+
+    private static int getImpliedSysUiFlagsForLayout(LayoutParams attrs) {
+        int impliedFlags = 0;
+        if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
+            impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+        }
+        final boolean forceWindowDrawsStatusBarBackground =
+                (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
+        if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
+                || forceWindowDrawsStatusBarBackground
+                && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) {
+            impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+        }
+        return impliedFlags;
+    }
+
+    private static boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) {
+        return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN
+                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0;
+    }
+
+    private final Runnable mClearHideNavigationFlag = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                mForceClearedSystemUiFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+                mDisplayContent.reevaluateStatusBarVisibility();
+            }
+        }
+    };
+
+    /**
+     * Input handler used while nav bar is hidden.  Captures any touch on the screen,
+     * to determine when the nav bar should be shown and prevent applications from
+     * receiving those touches.
+     */
+    private final class HideNavInputEventReceiver extends InputEventReceiver {
+        HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) {
+            super(inputChannel, looper);
+        }
+
+        @Override
+        public void onInputEvent(InputEvent event) {
+            try {
+                if (event instanceof MotionEvent
+                        && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+                    final MotionEvent motionEvent = (MotionEvent) event;
+                    if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
+                        // When the user taps down, we re-show the nav bar.
+                        boolean changed = false;
+                        synchronized (mLock) {
+                            if (mInputConsumer == null) {
+                                return;
+                            }
+                            // Any user activity always causes us to show the
+                            // navigation controls, if they had been hidden.
+                            // We also clear the low profile and only content
+                            // flags so that tapping on the screen will atomically
+                            // restore all currently hidden screen decorations.
+                            int newVal = mResettingSystemUiFlags
+                                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                                    | View.SYSTEM_UI_FLAG_LOW_PROFILE
+                                    | View.SYSTEM_UI_FLAG_FULLSCREEN;
+                            if (mResettingSystemUiFlags != newVal) {
+                                mResettingSystemUiFlags = newVal;
+                                changed = true;
+                            }
+                            // We don't allow the system's nav bar to be hidden
+                            // again for 1 second, to prevent applications from
+                            // spamming us and keeping it from being shown.
+                            newVal = mForceClearedSystemUiFlags
+                                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+                            if (mForceClearedSystemUiFlags != newVal) {
+                                mForceClearedSystemUiFlags = newVal;
+                                changed = true;
+                                mHandler.postDelayed(mClearHideNavigationFlag, 1000);
+                            }
+                            if (changed) {
+                                mDisplayContent.reevaluateStatusBarVisibility();
+                            }
+                        }
+                    }
+                }
+            } finally {
+                finishInputEvent(event, false /* handled */);
+            }
+        }
+    }
+
+    /**
+     * Called when layout of the windows is about to start.
+     *
+     * @param displayFrames frames of the display we are doing layout on.
+     * @param uiMode The current uiMode in configuration.
+     */
+    public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
+        displayFrames.onBeginLayout();
+        mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
+        mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
+
+        // For purposes of putting out fake window up to steal focus, we will
+        // drive nav being hidden only by whether it is requested.
+        final int sysui = mLastSystemUiFlags;
+        boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+        boolean navTranslucent = (sysui
+                & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
+        boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
+        boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
+        boolean navAllowedHidden = immersive || immersiveSticky;
+        navTranslucent &= !immersiveSticky;  // transient trumps translucent
+        boolean isKeyguardShowing = isStatusBarKeyguard()
+                && !mService.mPolicy.isKeyguardOccluded();
+        if (!isKeyguardShowing) {
+            navTranslucent &= areTranslucentBarsAllowed();
+        }
+        boolean statusBarForcesShowingNavigation = !isKeyguardShowing && mStatusBar != null
+                && (mStatusBar.getAttrs().privateFlags
+                & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
+
+        // When the navigation bar isn't visible, we put up a fake input window to catch all
+        // touch events. This way we can detect when the user presses anywhere to bring back the
+        // nav bar and ensure the application doesn't see the event.
+        if (navVisible || navAllowedHidden) {
+            if (mInputConsumer != null) {
+                mHandler.sendMessage(
+                        mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
+                mInputConsumer = null;
+            }
+        } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
+            mInputConsumer = mService.createInputConsumer(mHandler.getLooper(),
+                    INPUT_CONSUMER_NAVIGATION,
+                    HideNavInputEventReceiver::new,
+                    displayFrames.mDisplayId);
+            // As long as mInputConsumer is active, hover events are not dispatched to the app
+            // and the pointer icon is likely to become stale. Hide it to avoid confusion.
+            InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
+        }
+
+        // For purposes of positioning and showing the nav bar, if we have decided that it can't
+        // be hidden (because of the screen aspect ratio), then take that into account.
+        navVisible |= !canHideNavigationBar();
+
+        boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
+                navTranslucent, navAllowedHidden, statusBarForcesShowingNavigation);
+        if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
+        updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing);
+        if (updateSysUiVisibility) {
+            updateSystemUiVisibilityLw();
+        }
+        layoutScreenDecorWindows(displayFrames);
+
+        if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
+            // Make sure that the zone we're avoiding for the cutout is at least as tall as the
+            // status bar; otherwise fullscreen apps will end up cutting halfway into the status
+            // bar.
+            displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,
+                    displayFrames.mStable.top);
+        }
+    }
+
+    private void layoutScreenDecorWindows(DisplayFrames displayFrames) {
+        if (mScreenDecorWindows.isEmpty()) {
+            return;
+        }
+
+        sTmpRect.setEmpty();
+        sTmpDockedFrame.set(displayFrames.mDock);
+
+        final int displayId = displayFrames.mDisplayId;
+        final Rect dockFrame = displayFrames.mDock;
+        final int displayHeight = displayFrames.mDisplayHeight;
+        final int displayWidth = displayFrames.mDisplayWidth;
+
+        for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) {
+            final WindowState w = mScreenDecorWindows.valueAt(i);
+            if (w.getDisplayId() != displayId || !w.isVisibleLw()) {
+                // Skip if not on the same display or not visible.
+                continue;
+            }
+
+            w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */,
+                    sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */,
+                    sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */,
+                    sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */,
+                    sTmpDockedFrame /* outsetFrame */);
+            w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+            w.computeFrameLw();
+            final Rect frame = w.getFrameLw();
+
+            if (frame.left <= 0 && frame.top <= 0) {
+                // Docked at left or top.
+                if (frame.bottom >= displayHeight) {
+                    // Docked left.
+                    dockFrame.left = Math.max(frame.right, dockFrame.left);
+                } else if (frame.right >= displayWidth) {
+                    // Docked top.
+                    dockFrame.top = Math.max(frame.bottom, dockFrame.top);
+                } else {
+                    Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
+                            + " not docked on left or top of display. frame=" + frame
+                            + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
+                }
+            } else if (frame.right >= displayWidth && frame.bottom >= displayHeight) {
+                // Docked at right or bottom.
+                if (frame.top <= 0) {
+                    // Docked right.
+                    dockFrame.right = Math.min(frame.left, dockFrame.right);
+                } else if (frame.left <= 0) {
+                    // Docked bottom.
+                    dockFrame.bottom = Math.min(frame.top, dockFrame.bottom);
+                } else {
+                    Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
+                            + " not docked on right or bottom" + " of display. frame=" + frame
+                            + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
+                }
+            } else {
+                // Screen decor windows are required to be docked on one of the sides of the screen.
+                Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
+                        + " not docked on one of the sides of the display. frame=" + frame
+                        + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
+            }
+        }
+
+        displayFrames.mRestricted.set(dockFrame);
+        displayFrames.mCurrent.set(dockFrame);
+        displayFrames.mVoiceContent.set(dockFrame);
+        displayFrames.mSystem.set(dockFrame);
+        displayFrames.mContent.set(dockFrame);
+        displayFrames.mRestrictedOverscan.set(dockFrame);
+    }
+
+    private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
+            boolean isKeyguardShowing) {
+        // decide where the status bar goes ahead of time
+        if (mStatusBar == null) {
+            return false;
+        }
+        // apply any navigation bar insets
+        sTmpRect.setEmpty();
+        mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
+                displayFrames.mUnrestricted /* displayFrame */,
+                displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
+                displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
+                displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
+        mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+
+        // Let the status bar determine its size.
+        mStatusBar.computeFrameLw();
+
+        // For layout, the status bar is always at the top with our fixed height.
+        displayFrames.mStable.top = displayFrames.mUnrestricted.top
+                + mStatusBarHeightForRotation[displayFrames.mRotation];
+        // Make sure the status bar covers the entire cutout height
+        displayFrames.mStable.top = Math.max(displayFrames.mStable.top,
+                displayFrames.mDisplayCutoutSafe.top);
+
+        // Tell the bar controller where the collapsed status bar content is
+        sTmpRect.set(mStatusBar.getContentFrameLw());
+        sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+        sTmpRect.top = mStatusBar.getContentFrameLw().top;  // Ignore top display cutout inset
+        sTmpRect.bottom = displayFrames.mStable.top;  // Use collapsed status bar size
+        mStatusBarController.setContentFrame(sTmpRect);
+
+        boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
+        boolean statusBarTranslucent = (sysui
+                & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
+        if (!isKeyguardShowing) {
+            statusBarTranslucent &= areTranslucentBarsAllowed();
+        }
+
+        // If the status bar is hidden, we don't want to cause windows behind it to scroll.
+        if (mStatusBar.isVisibleLw() && !statusBarTransient) {
+            // Status bar may go away, so the screen area it occupies is available to apps but just
+            // covering them when the status bar is visible.
+            final Rect dockFrame = displayFrames.mDock;
+            dockFrame.top = displayFrames.mStable.top;
+            displayFrames.mContent.set(dockFrame);
+            displayFrames.mVoiceContent.set(dockFrame);
+            displayFrames.mCurrent.set(dockFrame);
+
+            if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
+                    "dock=%s content=%s cur=%s", dockFrame.toString(),
+                    displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
+
+            if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
+                    && !mStatusBarController.wasRecentlyTranslucent()) {
+                // If the opaque status bar is currently requested to be visible, and not in the
+                // process of animating on or off, then we can tell the app that it is covered by
+                // it.
+                displayFrames.mSystem.top = displayFrames.mStable.top;
+            }
+        }
+        return mStatusBarController.checkHiddenLw();
+    }
+
+    private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
+            boolean navTranslucent, boolean navAllowedHidden,
+            boolean statusBarForcesShowingNavigation) {
+        if (mNavigationBar == null) {
+            return false;
+        }
+
+        final Rect navigationFrame = sTmpNavFrame;
+        boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
+        // Force the navigation bar to its appropriate place and size. We need to do this directly,
+        // instead of relying on it to bubble up from the nav bar, because this needs to change
+        // atomically with screen rotations.
+        final int rotation = displayFrames.mRotation;
+        final int displayHeight = displayFrames.mDisplayHeight;
+        final int displayWidth = displayFrames.mDisplayWidth;
+        final Rect dockFrame = displayFrames.mDock;
+        mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
+
+        final Rect cutoutSafeUnrestricted = sTmpRect;
+        cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
+        cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
+
+        if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
+            // It's a system nav bar or a portrait screen; nav bar goes on bottom.
+            final int top = cutoutSafeUnrestricted.bottom
+                    - getNavigationBarHeight(rotation, uiMode);
+            // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+            final int topNavBar = cutoutSafeUnrestricted.bottom
+                    - mExperiments.getNavigationBarFrameHeight();
+            navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom);
+            // EXPERIMENT END
+            displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
+            if (transientNavBarShowing) {
+                mNavigationBarController.setBarShowingLw(true);
+            } else if (navVisible) {
+                mNavigationBarController.setBarShowingLw(true);
+                dockFrame.bottom = displayFrames.mRestricted.bottom =
+                        displayFrames.mRestrictedOverscan.bottom = top;
+            } else {
+                // We currently want to hide the navigation UI - unless we expanded the status bar.
+                mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
+            }
+            if (navVisible && !navTranslucent && !navAllowedHidden
+                    && !mNavigationBar.isAnimatingLw()
+                    && !mNavigationBarController.wasRecentlyTranslucent()) {
+                // If the opaque nav bar is currently requested to be visible and not in the process
+                // of animating on or off, then we can tell the app that it is covered by it.
+                displayFrames.mSystem.bottom = top;
+            }
+        } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+            // Landscape screen; nav bar goes to the right.
+            final int left = cutoutSafeUnrestricted.right
+                    - getNavigationBarWidth(rotation, uiMode);
+            // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+            final int leftNavBar = cutoutSafeUnrestricted.right
+                    - mExperiments.getNavigationBarFrameWidth();
+            navigationFrame.set(leftNavBar, 0, displayFrames.mUnrestricted.right, displayHeight);
+            // EXPERIMENT END
+            displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
+            if (transientNavBarShowing) {
+                mNavigationBarController.setBarShowingLw(true);
+            } else if (navVisible) {
+                mNavigationBarController.setBarShowingLw(true);
+                dockFrame.right = displayFrames.mRestricted.right =
+                        displayFrames.mRestrictedOverscan.right = left;
+            } else {
+                // We currently want to hide the navigation UI - unless we expanded the status bar.
+                mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
+            }
+            if (navVisible && !navTranslucent && !navAllowedHidden
+                    && !mNavigationBar.isAnimatingLw()
+                    && !mNavigationBarController.wasRecentlyTranslucent()) {
+                // If the nav bar is currently requested to be visible, and not in the process of
+                // animating on or off, then we can tell the app that it is covered by it.
+                displayFrames.mSystem.right = left;
+            }
+        } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+            // Seascape screen; nav bar goes to the left.
+            final int right = cutoutSafeUnrestricted.left
+                    + getNavigationBarWidth(rotation, uiMode);
+            // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+            final int rightNavBar = cutoutSafeUnrestricted.left
+                    + mExperiments.getNavigationBarFrameWidth();
+            navigationFrame.set(displayFrames.mUnrestricted.left, 0, rightNavBar, displayHeight);
+            // EXPERIMENT END
+            displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
+            if (transientNavBarShowing) {
+                mNavigationBarController.setBarShowingLw(true);
+            } else if (navVisible) {
+                mNavigationBarController.setBarShowingLw(true);
+                dockFrame.left = displayFrames.mRestricted.left =
+                        displayFrames.mRestrictedOverscan.left = right;
+            } else {
+                // We currently want to hide the navigation UI - unless we expanded the status bar.
+                mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
+            }
+            if (navVisible && !navTranslucent && !navAllowedHidden
+                    && !mNavigationBar.isAnimatingLw()
+                    && !mNavigationBarController.wasRecentlyTranslucent()) {
+                // If the nav bar is currently requested to be visible, and not in the process of
+                // animating on or off, then we can tell the app that it is covered by it.
+                displayFrames.mSystem.left = right;
+            }
+        }
+
+        // Make sure the content and current rectangles are updated to account for the restrictions
+        // from the navigation bar.
+        displayFrames.mCurrent.set(dockFrame);
+        displayFrames.mVoiceContent.set(dockFrame);
+        displayFrames.mContent.set(dockFrame);
+        // And compute the final frame.
+        sTmpRect.setEmpty();
+        mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */,
+                navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
+                displayFrames.mDisplayCutoutSafe /* contentFrame */,
+                navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
+                navigationFrame /* stableFrame */,
+                displayFrames.mDisplayCutoutSafe /* outsetFrame */);
+        mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+        mNavigationBar.computeFrameLw();
+        mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
+
+        if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
+        return mNavigationBarController.checkHiddenLw();
+    }
+
+    private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
+            boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf,
+            DisplayFrames displayFrames) {
+        if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) {
+            // Here's a special case: if the child window is not the 'dock window'
+            // or input method target, and the window it is attached to is below
+            // the dock window, then the frames we computed for the window it is
+            // attached to can not be used because the dock is effectively part
+            // of the underlying window and the attached window is floating on top
+            // of the whole thing. So, we ignore the attached window and explicitly
+            // compute the frames that would be appropriate without the dock.
+            vf.set(displayFrames.mDock);
+            cf.set(displayFrames.mDock);
+            of.set(displayFrames.mDock);
+            df.set(displayFrames.mDock);
+        } else {
+            // The effective display frame of the attached window depends on whether it is taking
+            // care of insetting its content. If not, we need to use the parent's content frame so
+            // that the entire window is positioned within that content. Otherwise we can use the
+            // overscan frame and let the attached window take care of positioning its content
+            // appropriately.
+            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+                // Set the content frame of the attached window to the parent's decor frame
+                // (same as content frame when IME isn't present) if specifically requested by
+                // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag.
+                // Otherwise, use the overscan frame.
+                cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0
+                        ? attached.getContentFrameLw() : attached.getOverscanFrameLw());
+            } else {
+                // If the window is resizing, then we want to base the content frame on our attached
+                // content frame to resize...however, things can be tricky if the attached window is
+                // NOT in resize mode, in which case its content frame will be larger.
+                // Ungh. So to deal with that, make sure the content frame we end up using is not
+                // covering the IM dock.
+                cf.set(attached.getContentFrameLw());
+                if (attached.isVoiceInteraction()) {
+                    cf.intersectUnchecked(displayFrames.mVoiceContent);
+                } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) {
+                    cf.intersectUnchecked(displayFrames.mContent);
+                }
+            }
+            df.set(insetDecors ? attached.getDisplayFrameLw() : cf);
+            of.set(insetDecors ? attached.getOverscanFrameLw() : cf);
+            vf.set(attached.getVisibleFrameLw());
+        }
+        // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be
+        // positioned relative to its parent or the entire screen.
+        pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df);
+    }
+
+    private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) {
+        if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) {
+            return;
+        }
+        // If app is requesting a stable layout, don't let the content insets go below the stable
+        // values.
+        if ((fl & FLAG_FULLSCREEN) != 0) {
+            r.intersectUnchecked(displayFrames.mStableFullscreen);
+        } else {
+            r.intersectUnchecked(displayFrames.mStable);
+        }
+    }
+
+    private boolean canReceiveInput(WindowState win) {
+        boolean notFocusable =
+                (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0;
+        boolean altFocusableIm =
+                (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0;
+        boolean notFocusableForIm = notFocusable ^ altFocusableIm;
+        return !notFocusableForIm;
+    }
+
+    /**
+     * Called for each window attached to the window manager as layout is proceeding. The
+     * implementation of this function must take care of setting the window's frame, either here or
+     * in finishLayout().
+     *
+     * @param win The window being positioned.
+     * @param attached For sub-windows, the window it is attached to; this
+     *                 window will already have had layoutWindow() called on it
+     *                 so you can use its Rect.  Otherwise null.
+     * @param displayFrames The display frames.
+     */
+    public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
+        // We've already done the navigation bar, status bar, and all screen decor windows. If the
+        // status bar can receive input, we need to layout it again to accommodate for the IME
+        // window.
+        if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar
+                || mScreenDecorWindows.contains(win)) {
+            return;
+        }
+        final WindowManager.LayoutParams attrs = win.getAttrs();
+        final boolean isDefaultDisplay = win.isDefaultDisplay();
+
+        final int type = attrs.type;
+        final int fl = PolicyControl.getWindowFlags(win, attrs);
+        final int pfl = attrs.privateFlags;
+        final int sim = attrs.softInputMode;
+        final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
+        final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
+
+        final WindowFrames windowFrames = win.getWindowFrames();
+
+        windowFrames.setHasOutsets(false);
+        sTmpLastParentFrame.set(windowFrames.mParentFrame);
+        final Rect pf = windowFrames.mParentFrame;
+        final Rect df = windowFrames.mDisplayFrame;
+        final Rect of = windowFrames.mOverscanFrame;
+        final Rect cf = windowFrames.mContentFrame;
+        final Rect vf = windowFrames.mVisibleFrame;
+        final Rect dcf = windowFrames.mDecorFrame;
+        final Rect sf = windowFrames.mStableFrame;
+        dcf.setEmpty();
+        windowFrames.setParentFrameWasClippedByDisplayCutout(false);
+        windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
+
+        final boolean hasNavBar = hasNavigationBar() && mNavigationBar != null
+                && mNavigationBar.isVisibleLw();
+
+        final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
+
+        final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
+                || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+
+        final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
+        final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
+
+        sf.set(displayFrames.mStable);
+
+        if (type == TYPE_INPUT_METHOD) {
+            vf.set(displayFrames.mDock);
+            cf.set(displayFrames.mDock);
+            of.set(displayFrames.mDock);
+            df.set(displayFrames.mDock);
+            windowFrames.mParentFrame.set(displayFrames.mDock);
+            // IM dock windows layout below the nav bar...
+            pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
+            // ...with content insets above the nav bar
+            cf.bottom = vf.bottom = displayFrames.mStable.bottom;
+            if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
+                // The status bar forces the navigation bar while it's visible. Make sure the IME
+                // avoids the navigation bar in that case.
+                if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+                    pf.right = df.right = of.right = cf.right = vf.right =
+                            displayFrames.mStable.right;
+                } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+                    pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
+                }
+            }
+
+            // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+            // Offset the ime to avoid overlapping with the nav bar
+            mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win);
+            // EXPERIMENT END
+
+            // IM dock windows always go to the bottom of the screen.
+            attrs.gravity = Gravity.BOTTOM;
+        } else if (type == TYPE_VOICE_INTERACTION) {
+            of.set(displayFrames.mUnrestricted);
+            df.set(displayFrames.mUnrestricted);
+            pf.set(displayFrames.mUnrestricted);
+            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+                cf.set(displayFrames.mDock);
+            } else {
+                cf.set(displayFrames.mContent);
+            }
+            if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+                vf.set(displayFrames.mCurrent);
+            } else {
+                vf.set(cf);
+            }
+        } else if (type == TYPE_WALLPAPER) {
+            layoutWallpaper(displayFrames, pf, df, of, cf);
+        } else if (win == mStatusBar) {
+            of.set(displayFrames.mUnrestricted);
+            df.set(displayFrames.mUnrestricted);
+            pf.set(displayFrames.mUnrestricted);
+            cf.set(displayFrames.mStable);
+            vf.set(displayFrames.mStable);
+
+            if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
+                cf.bottom = displayFrames.mContent.bottom;
+            } else {
+                cf.bottom = displayFrames.mDock.bottom;
+                vf.bottom = displayFrames.mContent.bottom;
+            }
+        } else {
+            dcf.set(displayFrames.mSystem);
+            final boolean inheritTranslucentDecor =
+                    (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
+            final boolean isAppWindow =
+                    type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
+            final boolean topAtRest =
+                    win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
+            if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
+                if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
+                        && (fl & FLAG_FULLSCREEN) == 0
+                        && (fl & FLAG_TRANSLUCENT_STATUS) == 0
+                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) {
+                    // Ensure policy decor includes status bar
+                    dcf.top = displayFrames.mStable.top;
+                }
+                if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
+                        && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
+                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
+                    // Ensure policy decor includes navigation bar
+                    dcf.bottom = displayFrames.mStable.bottom;
+                    dcf.right = displayFrames.mStable.right;
+                }
+            }
+
+            if (layoutInScreen && layoutInsetDecor) {
+                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+                        + "): IN_SCREEN, INSET_DECOR");
+                // This is the case for a normal activity window: we want it to cover all of the
+                // screen space, and it can take care of moving its contents to account for screen
+                // decorations that intrude into that space.
+                if (attached != null) {
+                    // If this window is attached to another, our display
+                    // frame is the same as the one we are attached to.
+                    setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf,
+                            displayFrames);
+                } else {
+                    if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
+                        // Status bar panels are the only windows who can go on top of the status
+                        // bar. They are protected by the STATUS_BAR_SERVICE permission, so they
+                        // have the same privileges as the status bar itself.
+                        //
+                        // However, they should still dodge the navigation bar if it exists.
+
+                        pf.left = df.left = of.left = hasNavBar
+                                ? displayFrames.mDock.left : displayFrames.mUnrestricted.left;
+                        pf.top = df.top = of.top = displayFrames.mUnrestricted.top;
+                        pf.right = df.right = of.right = hasNavBar
+                                ? displayFrames.mRestricted.right
+                                : displayFrames.mUnrestricted.right;
+                        pf.bottom = df.bottom = of.bottom = hasNavBar
+                                ? displayFrames.mRestricted.bottom
+                                : displayFrames.mUnrestricted.bottom;
+
+                        if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf);
+                    } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
+                            && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+                        // Asking to layout into the overscan region, so give it that pure
+                        // unrestricted area.
+                        of.set(displayFrames.mOverscan);
+                        df.set(displayFrames.mOverscan);
+                        pf.set(displayFrames.mOverscan);
+                    } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
+                            && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW
+                            || type == TYPE_VOLUME_OVERLAY)) {
+                        // Asking for layout as if the nav bar is hidden, lets the application
+                        // extend into the unrestricted overscan screen area. We only do this for
+                        // application windows and certain system windows to ensure no window that
+                        // can be above the nav bar can do this.
+                        df.set(displayFrames.mOverscan);
+                        pf.set(displayFrames.mOverscan);
+                        // We need to tell the app about where the frame inside the overscan is, so
+                        // it can inset its content by that amount -- it didn't ask to actually
+                        // extend itself into the overscan region.
+                        of.set(displayFrames.mUnrestricted);
+                    } else {
+                        df.set(displayFrames.mRestrictedOverscan);
+                        pf.set(displayFrames.mRestrictedOverscan);
+                        // We need to tell the app about where the frame inside the overscan
+                        // is, so it can inset its content by that amount -- it didn't ask
+                        // to actually extend itself into the overscan region.
+                        of.set(displayFrames.mUnrestricted);
+                    }
+
+                    if ((fl & FLAG_FULLSCREEN) == 0) {
+                        if (win.isVoiceInteraction()) {
+                            cf.set(displayFrames.mVoiceContent);
+                        } else {
+                            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+                                cf.set(displayFrames.mDock);
+                            } else {
+                                cf.set(displayFrames.mContent);
+                            }
+                        }
+                    } else {
+                        // Full screen windows are always given a layout that is as if the status
+                        // bar and other transient decors are gone. This is to avoid bad states when
+                        // moving from a window that is not hiding the status bar to one that is.
+                        cf.set(displayFrames.mRestricted);
+                    }
+                    applyStableConstraints(sysUiFl, fl, cf, displayFrames);
+                    if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+                        vf.set(displayFrames.mCurrent);
+                    } else {
+                        vf.set(cf);
+                    }
+
+                    // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+                    mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win);
+                    // EXPERIMENT END
+                }
+            } else if (layoutInScreen || (sysUiFl
+                    & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
+                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+                        + "): IN_SCREEN");
+                // A window that has requested to fill the entire screen just
+                // gets everything, period.
+                if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
+                    cf.set(displayFrames.mUnrestricted);
+                    of.set(displayFrames.mUnrestricted);
+                    df.set(displayFrames.mUnrestricted);
+                    pf.set(displayFrames.mUnrestricted);
+                    if (hasNavBar) {
+                        pf.left = df.left = of.left = cf.left = displayFrames.mDock.left;
+                        pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right;
+                        pf.bottom = df.bottom = of.bottom = cf.bottom =
+                                displayFrames.mRestricted.bottom;
+                    }
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf);
+                } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
+                    // The navigation bar has Real Ultimate Power.
+                    of.set(displayFrames.mUnrestricted);
+                    df.set(displayFrames.mUnrestricted);
+                    pf.set(displayFrames.mUnrestricted);
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf);
+                } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
+                        && ((fl & FLAG_FULLSCREEN) != 0)) {
+                    // Fullscreen secure system overlays get what they ask for. Screenshot region
+                    // selection overlay should also expand to full screen.
+                    cf.set(displayFrames.mOverscan);
+                    of.set(displayFrames.mOverscan);
+                    df.set(displayFrames.mOverscan);
+                    pf.set(displayFrames.mOverscan);
+                } else if (type == TYPE_BOOT_PROGRESS) {
+                    // Boot progress screen always covers entire display.
+                    cf.set(displayFrames.mOverscan);
+                    of.set(displayFrames.mOverscan);
+                    df.set(displayFrames.mOverscan);
+                    pf.set(displayFrames.mOverscan);
+                } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
+                        && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+                    // Asking to layout into the overscan region, so give it that pure unrestricted
+                    // area.
+                    cf.set(displayFrames.mOverscan);
+                    of.set(displayFrames.mOverscan);
+                    df.set(displayFrames.mOverscan);
+                    pf.set(displayFrames.mOverscan);
+                } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
+                        && (type == TYPE_STATUS_BAR
+                        || type == TYPE_TOAST
+                        || type == TYPE_DOCK_DIVIDER
+                        || type == TYPE_VOICE_INTERACTION_STARTING
+                        || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) {
+                    // Asking for layout as if the nav bar is hidden, lets the
+                    // application extend into the unrestricted screen area.  We
+                    // only do this for application windows (or toasts) to ensure no window that
+                    // can be above the nav bar can do this.
+                    // XXX This assumes that an app asking for this will also
+                    // ask for layout in only content.  We can't currently figure out
+                    // what the screen would be if only laying out to hide the nav bar.
+                    cf.set(displayFrames.mUnrestricted);
+                    of.set(displayFrames.mUnrestricted);
+                    df.set(displayFrames.mUnrestricted);
+                    pf.set(displayFrames.mUnrestricted);
+                } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
+                    of.set(displayFrames.mRestricted);
+                    df.set(displayFrames.mRestricted);
+                    pf.set(displayFrames.mRestricted);
+                    if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+                        cf.set(displayFrames.mDock);
+                    } else {
+                        cf.set(displayFrames.mContent);
+                    }
+                } else {
+                    cf.set(displayFrames.mRestricted);
+                    of.set(displayFrames.mRestricted);
+                    df.set(displayFrames.mRestricted);
+                    pf.set(displayFrames.mRestricted);
+                }
+
+                applyStableConstraints(sysUiFl, fl, cf, displayFrames);
+
+                if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+                    vf.set(displayFrames.mCurrent);
+                } else {
+                    vf.set(cf);
+                }
+            } else if (attached != null) {
+                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+                        + "): attached to " + attached);
+                // A child window should be placed inside of the same visible
+                // frame that its parent had.
+                setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf,
+                        displayFrames);
+            } else {
+                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+                        + "): normal window");
+                // Otherwise, a normal window must be placed inside the content
+                // of all screen decorations.
+                if (type == TYPE_STATUS_BAR_PANEL) {
+                    // Status bar panels can go on
+                    // top of the status bar. They are protected by the STATUS_BAR_SERVICE
+                    // permission, so they have the same privileges as the status bar itself.
+                    cf.set(displayFrames.mRestricted);
+                    of.set(displayFrames.mRestricted);
+                    df.set(displayFrames.mRestricted);
+                    pf.set(displayFrames.mRestricted);
+                } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
+                    // These dialogs are stable to interim decor changes.
+                    cf.set(displayFrames.mStable);
+                    of.set(displayFrames.mStable);
+                    df.set(displayFrames.mStable);
+                    pf.set(displayFrames.mStable);
+                } else {
+                    pf.set(displayFrames.mContent);
+                    if (win.isVoiceInteraction()) {
+                        cf.set(displayFrames.mVoiceContent);
+                        of.set(displayFrames.mVoiceContent);
+                        df.set(displayFrames.mVoiceContent);
+                    } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+                        cf.set(displayFrames.mDock);
+                        of.set(displayFrames.mDock);
+                        df.set(displayFrames.mDock);
+                    } else {
+                        cf.set(displayFrames.mContent);
+                        of.set(displayFrames.mContent);
+                        df.set(displayFrames.mContent);
+                    }
+                    if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+                        vf.set(displayFrames.mCurrent);
+                    } else {
+                        vf.set(cf);
+                    }
+                }
+            }
+        }
+
+        final int cutoutMode = attrs.layoutInDisplayCutoutMode;
+        final boolean attachedInParent = attached != null && !layoutInScreen;
+        final boolean requestedHideNavigation =
+                (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
+
+        // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get
+        // cropped / shifted to the displayFrame in WindowState.
+        final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
+                && type != TYPE_BASE_APPLICATION;
+
+        // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
+        // the cutout safe zone.
+        if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
+            final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
+            displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
+            if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
+                    && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
+                // At the top we have the status bar, so apps that are
+                // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
+                // already expect that there's an inset there and we don't need to exclude
+                // the window from that area.
+                displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
+            }
+            if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation
+                    && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
+                // Same for the navigation bar.
+                switch (mNavigationBarPosition) {
+                    case NAV_BAR_BOTTOM:
+                        displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+                        break;
+                    case NAV_BAR_RIGHT:
+                        displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+                        break;
+                    case NAV_BAR_LEFT:
+                        displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
+                        break;
+                }
+            }
+            if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) {
+                // The IME can always extend under the bottom cutout if the navbar is there.
+                displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+            }
+            // Windows that are attached to a parent and laid out in said parent already avoid
+            // the cutout according to that parent and don't need to be further constrained.
+            // Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
+            // They will later be cropped or shifted using the displayFrame in WindowState,
+            // which prevents overlap with the DisplayCutout.
+            if (!attachedInParent && !floatingInScreenWindow) {
+                sTmpRect.set(pf);
+                pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+                windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
+            }
+            // Make sure that NO_LIMITS windows clipped to the display don't extend under the
+            // cutout.
+            df.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+        }
+
+        // Content should never appear in the cutout.
+        cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
+
+        // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
+        // Also, we don't allow windows in multi-window mode to extend out of the screen.
+        if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
+                && !win.isInMultiWindowMode()) {
+            df.left = df.top = -10000;
+            df.right = df.bottom = 10000;
+            if (type != TYPE_WALLPAPER) {
+                of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000;
+                of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
+            }
+        }
+
+        // If the device has a chin (e.g. some watches), a dead area at the bottom of the screen we
+        // need to provide information to the clients that want to pretend that you can draw there.
+        // We only want to apply outsets to certain types of windows. For example, we never want to
+        // apply the outsets to floating dialogs, because they wouldn't make sense there.
+        final boolean useOutsets = shouldUseOutsets(attrs, fl);
+        if (isDefaultDisplay && useOutsets) {
+            final Rect osf = windowFrames.mOutsetFrame;
+            osf.set(cf.left, cf.top, cf.right, cf.bottom);
+            windowFrames.setHasOutsets(true);
+            int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
+            if (outset > 0) {
+                int rotation = displayFrames.mRotation;
+                if (rotation == Surface.ROTATION_0) {
+                    osf.bottom += outset;
+                } else if (rotation == Surface.ROTATION_90) {
+                    osf.right += outset;
+                } else if (rotation == Surface.ROTATION_180) {
+                    osf.top -= outset;
+                } else if (rotation == Surface.ROTATION_270) {
+                    osf.left -= outset;
+                }
+                if (DEBUG_LAYOUT) Slog.v(TAG, "applying bottom outset of " + outset
+                        + " with rotation " + rotation + ", result: " + osf);
+            }
+        }
+
+        if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
+                + ": sim=#" + Integer.toHexString(sim)
+                + " attach=" + attached + " type=" + type
+                + String.format(" flags=0x%08x", fl)
+                + " pf=" + pf.toShortString() + " df=" + df.toShortString()
+                + " of=" + of.toShortString()
+                + " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
+                + " dcf=" + dcf.toShortString()
+                + " sf=" + sf.toShortString()
+                + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win);
+
+        if (!sTmpLastParentFrame.equals(pf)) {
+            windowFrames.setContentChanged(true);
+        }
+
+        win.computeFrameLw();
+        // Dock windows carve out the bottom of the screen, so normal windows
+        // can't appear underneath them.
+        if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
+                && !win.getGivenInsetsPendingLw()) {
+            offsetInputMethodWindowLw(win, displayFrames);
+        }
+        if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
+                && !win.getGivenInsetsPendingLw()) {
+            offsetVoiceInputWindowLw(win, displayFrames);
+        }
+    }
+
+    private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) {
+        // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area.
+        df.set(displayFrames.mOverscan);
+        pf.set(displayFrames.mOverscan);
+        cf.set(displayFrames.mUnrestricted);
+        of.set(displayFrames.mUnrestricted);
+    }
+
+    private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
+        int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
+        top += win.getGivenContentInsetsLw().top;
+        displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
+        displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
+        top = win.getVisibleFrameLw().top;
+        top += win.getGivenVisibleInsetsLw().top;
+        displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top);
+        if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom="
+                + displayFrames.mDock.bottom + " mContentBottom="
+                + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom);
+    }
+
+    private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) {
+        int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
+        top += win.getGivenContentInsetsLw().top;
+        displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
+    }
+
+    /**
+     * Called following layout of all windows before each window has policy applied.
+     */
+    public void beginPostLayoutPolicyLw() {
+        mTopFullscreenOpaqueWindowState = null;
+        mTopFullscreenOpaqueOrDimmingWindowState = null;
+        mTopDockedOpaqueWindowState = null;
+        mTopDockedOpaqueOrDimmingWindowState = null;
+        mForceStatusBar = false;
+        mForceStatusBarFromKeyguard = false;
+        mForceStatusBarTransparent = false;
+        mForcingShowNavBar = false;
+        mForcingShowNavBarLayer = -1;
+
+        mAllowLockscreenWhenOn = false;
+        mShowingDream = false;
+        mWindowSleepTokenNeeded = false;
+    }
+
+    /**
+     * Called following layout of all window to apply policy to each window.
+     *
+     * @param win The window being positioned.
+     * @param attrs The LayoutParams of the window.
+     * @param attached For sub-windows, the window it is attached to. Otherwise null.
+     */
+    public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
+            WindowState attached, WindowState imeTarget) {
+        final boolean affectsSystemUi = win.canAffectSystemUiFlags();
+        if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
+        mService.mPolicy.applyKeyguardPolicyLw(win, imeTarget);
+        final int fl = PolicyControl.getWindowFlags(win, attrs);
+        if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi
+                && attrs.type == TYPE_INPUT_METHOD) {
+            mForcingShowNavBar = true;
+            mForcingShowNavBarLayer = win.getSurfaceLayer();
+        }
+        if (attrs.type == TYPE_STATUS_BAR) {
+            if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+                mForceStatusBarFromKeyguard = true;
+            }
+            if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
+                mForceStatusBarTransparent = true;
+            }
+        }
+
+        boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
+                && attrs.type < FIRST_SYSTEM_WINDOW;
+        final int windowingMode = win.getWindowingMode();
+        final boolean inFullScreenOrSplitScreenSecondaryWindowingMode =
+                windowingMode == WINDOWING_MODE_FULLSCREEN
+                        || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+        if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) {
+            if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
+                mForceStatusBar = true;
+            }
+            if (attrs.type == TYPE_DREAM) {
+                // If the lockscreen was showing when the dream started then wait
+                // for the dream to draw before hiding the lockscreen.
+                if (!mDreamingLockscreen
+                        || (win.isVisibleLw() && win.hasDrawnLw())) {
+                    mShowingDream = true;
+                    appWindow = true;
+                }
+            }
+
+            // For app windows that are not attached, we decide if all windows in the app they
+            // represent should be hidden or if we should hide the lockscreen. For attached app
+            // windows we defer the decision to the window it is attached to.
+            if (appWindow && attached == null) {
+                if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
+                    mTopFullscreenOpaqueWindowState = win;
+                    if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
+                        mTopFullscreenOpaqueOrDimmingWindowState = win;
+                    }
+                    if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
+                        mAllowLockscreenWhenOn = true;
+                    }
+                }
+            }
+        }
+
+        // Voice interaction overrides both top fullscreen and top docked.
+        if (affectsSystemUi && win.getAttrs().type == TYPE_VOICE_INTERACTION) {
+            if (mTopFullscreenOpaqueWindowState == null) {
+                mTopFullscreenOpaqueWindowState = win;
+                if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
+                    mTopFullscreenOpaqueOrDimmingWindowState = win;
+                }
+            }
+            if (mTopDockedOpaqueWindowState == null) {
+                mTopDockedOpaqueWindowState = win;
+                if (mTopDockedOpaqueOrDimmingWindowState == null) {
+                    mTopDockedOpaqueOrDimmingWindowState = win;
+                }
+            }
+        }
+
+        // Keep track of the window if it's dimming but not necessarily fullscreen.
+        if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi
+                && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
+            mTopFullscreenOpaqueOrDimmingWindowState = win;
+        }
+
+        // We need to keep track of the top "fullscreen" opaque window for the docked stack
+        // separately, because both the "real fullscreen" opaque window and the one for the docked
+        // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+        if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
+                && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            mTopDockedOpaqueWindowState = win;
+            if (mTopDockedOpaqueOrDimmingWindowState == null) {
+                mTopDockedOpaqueOrDimmingWindowState = win;
+            }
+        }
+
+        // Also keep track of any windows that are dimming but not necessarily fullscreen in the
+        // docked stack.
+        if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming()
+                && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            mTopDockedOpaqueOrDimmingWindowState = win;
+        }
+
+        // Take note if a window wants to acquire a sleep token.
+        if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0
+                && win.canAcquireSleepToken()) {
+            mWindowSleepTokenNeeded = true;
+        }
+    }
+
+    /**
+     * Called following layout of all windows and after policy has been applied
+     * to each window. If in this function you do
+     * something that may have modified the animation state of another window,
+     * be sure to return non-zero in order to perform another pass through layout.
+     *
+     * @return Return any bit set of
+     *         {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_LAYOUT},
+     *         {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_CONFIG},
+     *         {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER}, or
+     *         {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
+     */
+    public int finishPostLayoutPolicyLw() {
+        int changes = 0;
+        boolean topIsFullscreen = false;
+
+        // If we are not currently showing a dream then remember the current
+        // lockscreen state.  We will use this to determine whether the dream
+        // started while the lockscreen was showing and remember this state
+        // while the dream is showing.
+        if (!mShowingDream) {
+            mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded();
+            if (mDreamingSleepTokenNeeded) {
+                mDreamingSleepTokenNeeded = false;
+                mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget();
+            }
+        } else {
+            if (!mDreamingSleepTokenNeeded) {
+                mDreamingSleepTokenNeeded = true;
+                mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget();
+            }
+        }
+
+        if (mStatusBar != null) {
+            if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar
+                    + " forcefkg=" + mForceStatusBarFromKeyguard
+                    + " top=" + mTopFullscreenOpaqueWindowState);
+            boolean shouldBeTransparent = mForceStatusBarTransparent
+                    && !mForceStatusBar
+                    && !mForceStatusBarFromKeyguard;
+            if (!shouldBeTransparent) {
+                mStatusBarController.setShowTransparent(false /* transparent */);
+            } else if (!mStatusBar.isVisibleLw()) {
+                mStatusBarController.setShowTransparent(true /* transparent */);
+            }
+
+            boolean statusBarForcesShowingNavigation =
+                    (mStatusBar.getAttrs().privateFlags
+                            & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
+            boolean topAppHidesStatusBar = topAppHidesStatusBar();
+            if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent
+                    || statusBarForcesShowingNavigation) {
+                if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
+                if (mStatusBarController.setBarShowingLw(true)) {
+                    changes |= FINISH_LAYOUT_REDO_LAYOUT;
+                }
+                // Maintain fullscreen layout until incoming animation is complete.
+                topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw();
+                // Transient status bar is not allowed if status bar is on lockscreen or status bar
+                // is expecting the navigation keys from the user.
+                if ((mForceStatusBarFromKeyguard || statusBarForcesShowingNavigation)
+                        && mStatusBarController.isTransientShowing()) {
+                    mStatusBarController.updateVisibilityLw(false /*transientAllowed*/,
+                            mLastSystemUiFlags, mLastSystemUiFlags);
+                }
+            } else if (mTopFullscreenOpaqueWindowState != null) {
+                topIsFullscreen = topAppHidesStatusBar;
+                // The subtle difference between the window for mTopFullscreenOpaqueWindowState
+                // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
+                // has the FLAG_FULLSCREEN set.  Not sure if there is another way that to be the
+                // case though.
+                if (mStatusBarController.isTransientShowing()) {
+                    if (mStatusBarController.setBarShowingLw(true)) {
+                        changes |= FINISH_LAYOUT_REDO_LAYOUT;
+                    }
+                } else if (topIsFullscreen
+                        && !mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM)
+                        && !mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
+                    if (mStatusBarController.setBarShowingLw(false)) {
+                        changes |= FINISH_LAYOUT_REDO_LAYOUT;
+                    } else {
+                        if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding");
+                    }
+                } else {
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen");
+                    if (mStatusBarController.setBarShowingLw(true)) {
+                        changes |= FINISH_LAYOUT_REDO_LAYOUT;
+                    }
+                    topAppHidesStatusBar = false;
+                }
+            }
+            mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar);
+        }
+
+        if (mTopIsFullscreen != topIsFullscreen) {
+            if (!topIsFullscreen) {
+                // Force another layout when status bar becomes fully shown.
+                changes |= FINISH_LAYOUT_REDO_LAYOUT;
+            }
+            mTopIsFullscreen = topIsFullscreen;
+        }
+
+        if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+            // If the navigation bar has been hidden or shown, we need to do another
+            // layout pass to update that window.
+            changes |= FINISH_LAYOUT_REDO_LAYOUT;
+        }
+
+        if (mShowingDream != mLastShowingDream) {
+            mLastShowingDream = mShowingDream;
+            mService.notifyShowingDreamChanged();
+        }
+
+        updateWindowSleepToken();
+
+        mService.mPolicy.setAllowLockscreenWhenOn(getDisplayId(), mAllowLockscreenWhenOn);
+        return changes;
+    }
+
+    private void updateWindowSleepToken() {
+        if (mWindowSleepTokenNeeded && !mLastWindowSleepTokenNeeded) {
+            mHandler.removeCallbacks(mReleaseSleepTokenRunnable);
+            mHandler.post(mAcquireSleepTokenRunnable);
+        } else if (!mWindowSleepTokenNeeded && mLastWindowSleepTokenNeeded) {
+            mHandler.removeCallbacks(mAcquireSleepTokenRunnable);
+            mHandler.post(mReleaseSleepTokenRunnable);
+        }
+        mLastWindowSleepTokenNeeded = mWindowSleepTokenNeeded;
+    }
+
+    /**
+     * @return Whether the top app should hide the statusbar based on the top fullscreen opaque
+     *         window.
+     */
+    private boolean topAppHidesStatusBar() {
+        if (mTopFullscreenOpaqueWindowState == null) {
+            return false;
+        }
+        final int fl = PolicyControl.getWindowFlags(null,
+                mTopFullscreenOpaqueWindowState.getAttrs());
+        if (localLOGV) {
+            Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw());
+            Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+                    + " lp.flags=0x" + Integer.toHexString(fl));
+        }
+        return (fl & LayoutParams.FLAG_FULLSCREEN) != 0
+                || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+    }
+
+    /**
+     * Called when the resource overlays change.
+     */
+    public void onOverlayChangedLw() {
+        onConfigurationChanged();
+    }
+
+    /**
+     * Called when the configuration has changed, and it's safe to load new values from resources.
+     */
+    public void onConfigurationChanged() {
+        final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+
+        final Context uiContext = getSystemUiContext();
+        final Resources res = uiContext.getResources();
+        final int portraitRotation = displayRotation.getPortraitRotation();
+        final int upsideDownRotation = displayRotation.getUpsideDownRotation();
+        final int landscapeRotation = displayRotation.getLandscapeRotation();
+        final int seascapeRotation = displayRotation.getSeascapeRotation();
+
+        mStatusBarHeightForRotation[portraitRotation] =
+        mStatusBarHeightForRotation[upsideDownRotation] =
+                res.getDimensionPixelSize(R.dimen.status_bar_height_portrait);
+        mStatusBarHeightForRotation[landscapeRotation] =
+        mStatusBarHeightForRotation[seascapeRotation] =
+                res.getDimensionPixelSize(R.dimen.status_bar_height_landscape);
+
+        // Height of the navigation bar when presented horizontally at bottom
+        mNavigationBarHeightForRotationDefault[portraitRotation] =
+        mNavigationBarHeightForRotationDefault[upsideDownRotation] =
+                res.getDimensionPixelSize(R.dimen.navigation_bar_height);
+        mNavigationBarHeightForRotationDefault[landscapeRotation] =
+        mNavigationBarHeightForRotationDefault[seascapeRotation] =
+                res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape);
+
+        // Width of the navigation bar when presented vertically along one side
+        mNavigationBarWidthForRotationDefault[portraitRotation] =
+        mNavigationBarWidthForRotationDefault[upsideDownRotation] =
+        mNavigationBarWidthForRotationDefault[landscapeRotation] =
+        mNavigationBarWidthForRotationDefault[seascapeRotation] =
+                res.getDimensionPixelSize(R.dimen.navigation_bar_width);
+
+        if (ALTERNATE_CAR_MODE_NAV_SIZE) {
+            // Height of the navigation bar when presented horizontally at bottom
+            mNavigationBarHeightForRotationInCarMode[portraitRotation] =
+            mNavigationBarHeightForRotationInCarMode[upsideDownRotation] =
+                    res.getDimensionPixelSize(R.dimen.navigation_bar_height_car_mode);
+            mNavigationBarHeightForRotationInCarMode[landscapeRotation] =
+            mNavigationBarHeightForRotationInCarMode[seascapeRotation] =
+                    res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape_car_mode);
+
+            // Width of the navigation bar when presented vertically along one side
+            mNavigationBarWidthForRotationInCarMode[portraitRotation] =
+            mNavigationBarWidthForRotationInCarMode[upsideDownRotation] =
+            mNavigationBarWidthForRotationInCarMode[landscapeRotation] =
+            mNavigationBarWidthForRotationInCarMode[seascapeRotation] =
+                    res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode);
+        }
+
+        // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+        mExperiments.onConfigurationChanged(uiContext);
+        // EXPERIMENT END
+    }
+
+    @VisibleForTesting
+    Context getSystemUiContext() {
+        final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+        return mDisplayContent.isDefaultDisplay
+                ? uiContext : uiContext.createDisplayContext(mDisplayContent.getDisplay());
+    }
+
+    private int getNavigationBarWidth(int rotation, int uiMode) {
+        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+            return mNavigationBarWidthForRotationInCarMode[rotation];
+        } else {
+            return mNavigationBarWidthForRotationDefault[rotation];
+        }
+    }
+
+    /**
+     * Return the display width available after excluding any screen
+     * decorations that could never be removed in Honeycomb. That is, system bar or
+     * button bar.
+     */
+    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
+            DisplayCutout displayCutout) {
+        int width = fullWidth;
+        if (hasNavigationBar()) {
+            // For a basic navigation bar, when we are in landscape mode we place
+            // the navigation bar to the side.
+            if (navigationBarCanMove() && fullWidth > fullHeight) {
+                width -= getNavigationBarWidth(rotation, uiMode);
+            }
+        }
+        if (displayCutout != null) {
+            width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
+        }
+        return width;
+    }
+
+    private int getNavigationBarHeight(int rotation, int uiMode) {
+        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+            return mNavigationBarHeightForRotationInCarMode[rotation];
+        } else {
+            return mNavigationBarHeightForRotationDefault[rotation];
+        }
+    }
+
+    /**
+     * Return the display height available after excluding any screen
+     * decorations that could never be removed in Honeycomb. That is, system bar or
+     * button bar.
+     */
+    public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
+            DisplayCutout displayCutout) {
+        int height = fullHeight;
+        if (hasNavigationBar()) {
+            // For a basic navigation bar, when we are in portrait mode we place
+            // the navigation bar to the bottom.
+            if (!navigationBarCanMove() || fullWidth < fullHeight) {
+                height -= getNavigationBarHeight(rotation, uiMode);
+            }
+        }
+        if (displayCutout != null) {
+            height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();
+        }
+        return height;
+    }
+
+    /**
+     * Return the available screen width that we should report for the
+     * configuration.  This must be no larger than
+     * {@link #getNonDecorDisplayWidth(int, int, int, int, DisplayCutout)}; it may be smaller
+     * than that to account for more transient decoration like a status bar.
+     */
+    public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
+            DisplayCutout displayCutout) {
+        return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayCutout);
+    }
+
+    /**
+     * Return the available screen height that we should report for the
+     * configuration.  This must be no larger than
+     * {@link #getNonDecorDisplayHeight(int, int, int, int, DisplayCutout)}; it may be smaller
+     * than that to account for more transient decoration like a status bar.
+     */
+    public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
+            DisplayCutout displayCutout) {
+        // There is a separate status bar at the top of the display.  We don't count that as part
+        // of the fixed decor, since it can hide; however, for purposes of configurations,
+        // we do want to exclude it since applications can't generally use that part
+        // of the screen.
+        int statusBarHeight = mStatusBarHeightForRotation[rotation];
+        if (displayCutout != null) {
+            // If there is a cutout, it may already have accounted for some part of the status
+            // bar height.
+            statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
+        }
+        return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayCutout)
+                - statusBarHeight;
+    }
+
+    boolean isShowingDreamLw() {
+        return mShowingDream;
+    }
+
+    /**
+     * Calculates the stable insets without running a layout.
+     *
+     * @param displayRotation the current display rotation
+     * @param displayWidth the current display width
+     * @param displayHeight the current display height
+     * @param displayCutout the current display cutout
+     * @param outInsets the insets to return
+     */
+    public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+            DisplayCutout displayCutout, Rect outInsets) {
+        outInsets.setEmpty();
+
+        // Navigation bar and status bar.
+        getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets);
+        outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]);
+    }
+
+    /**
+     * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
+     * bar or button bar. See {@link #getNonDecorDisplayWidth}.
+     *
+     * @param displayRotation the current display rotation
+     * @param displayWidth the current display width
+     * @param displayHeight the current display height
+     * @param displayCutout the current display cutout
+     * @param outInsets the insets to return
+     */
+    public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+            DisplayCutout displayCutout, Rect outInsets) {
+        outInsets.setEmpty();
+
+        // Only navigation bar
+        if (hasNavigationBar()) {
+            final int uiMode = mService.mPolicy.getUiMode();
+            int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
+            if (position == NAV_BAR_BOTTOM) {
+                outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode);
+            } else if (position == NAV_BAR_RIGHT) {
+                outInsets.right = getNavigationBarWidth(displayRotation, uiMode);
+            } else if (position == NAV_BAR_LEFT) {
+                outInsets.left = getNavigationBarWidth(displayRotation, uiMode);
+            }
+        }
+
+        if (displayCutout != null) {
+            outInsets.left += displayCutout.getSafeInsetLeft();
+            outInsets.top += displayCutout.getSafeInsetTop();
+            outInsets.right += displayCutout.getSafeInsetRight();
+            outInsets.bottom += displayCutout.getSafeInsetBottom();
+        }
+    }
+
+    @NavigationBarPosition
+    int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
+        if (navigationBarCanMove() && displayWidth > displayHeight) {
+            if (displayRotation == Surface.ROTATION_270) {
+                return NAV_BAR_LEFT;
+            } else if (displayRotation == Surface.ROTATION_90) {
+                return NAV_BAR_RIGHT;
+            }
+        }
+        return NAV_BAR_BOTTOM;
+    }
+
+    /**
+     * @return The side of the screen where navigation bar is positioned.
+     * @see WindowManagerPolicyConstants#NAV_BAR_LEFT
+     * @see WindowManagerPolicyConstants#NAV_BAR_RIGHT
+     * @see WindowManagerPolicyConstants#NAV_BAR_BOTTOM
+     */
+    @NavigationBarPosition
+    public int getNavBarPosition() {
+        return mNavigationBarPosition;
+    }
+
+    /**
+     * A new window has been focused.
+     */
+    public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
+        mFocusedWindow = newFocus;
+        mLastFocusedWindow = lastFocus;
+        if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+            // If the navigation bar has been hidden or shown, we need to do another
+            // layout pass to update that window.
+            return FINISH_LAYOUT_REDO_LAYOUT;
+        }
+        return 0;
+    }
+
+    /**
+     * Return true if it is okay to perform animations for an app transition
+     * that is about to occur. You may return false for this if, for example,
+     * the dream window is currently displayed so the switch should happen
+     * immediately.
+     */
+    public boolean allowAppAnimationsLw() {
+        return !mShowingDream;
+    }
+
+    private void updateDreamingSleepToken(boolean acquire) {
+        if (acquire) {
+            final int displayId = getDisplayId();
+            if (mDreamingSleepToken == null) {
+                mDreamingSleepToken = mService.mAtmInternal.acquireSleepToken(
+                        "DreamOnDisplay" + displayId, displayId);
+            }
+        } else {
+            if (mDreamingSleepToken != null) {
+                mDreamingSleepToken.release();
+                mDreamingSleepToken = null;
+            }
+        }
+    }
+
+    private void requestTransientBars(WindowState swipeTarget) {
+        synchronized (mLock) {
+            if (!mService.mPolicy.isUserSetupComplete()) {
+                // Swipe-up for navigation bar is disabled during setup
+                return;
+            }
+            boolean sb = mStatusBarController.checkShowTransientBarLw();
+            boolean nb = mNavigationBarController.checkShowTransientBarLw()
+                    && !isNavBarEmpty(mLastSystemUiFlags);
+            if (sb || nb) {
+                // Don't show status bar when swiping on already visible navigation bar
+                if (!nb && swipeTarget == mNavigationBar) {
+                    if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
+                    return;
+                }
+                if (sb) mStatusBarController.showTransient();
+                if (nb) mNavigationBarController.showTransient();
+                mImmersiveModeConfirmation.confirmCurrentPrompt();
+                updateSystemUiVisibilityLw();
+            }
+        }
+    }
+
+    private void disposeInputConsumer(InputConsumer inputConsumer) {
+        if (inputConsumer != null) {
+            inputConsumer.dismiss();
+        }
+    }
+
+    private boolean isStatusBarKeyguard() {
+        return mStatusBar != null
+                && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
+    }
+
+    private boolean isKeyguardOccluded() {
+        // TODO (b/113840485): Handle per display keyguard.
+        return mService.mPolicy.isKeyguardOccluded();
+    }
+
+    void resetSystemUiVisibilityLw() {
+        mLastSystemUiFlags = 0;
+        updateSystemUiVisibilityLw();
+    }
+
+    private int updateSystemUiVisibilityLw() {
+        // If there is no window focused, there will be nobody to handle the events
+        // anyway, so just hang on in whatever state we're in until things settle down.
+        WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
+                : mTopFullscreenOpaqueWindowState;
+        if (winCandidate == null) {
+            return 0;
+        }
+
+        // The immersive mode confirmation should never affect the system bar visibility, otherwise
+        // it will unhide the navigation bar and hide itself.
+        if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
+
+            // The immersive mode confirmation took the focus from mLastFocusedWindow which was
+            // controlling the system ui visibility. So if mLastFocusedWindow can still receive
+            // keys, we let it keep controlling the visibility.
+            final boolean lastFocusCanReceiveKeys =
+                    (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
+            winCandidate = isStatusBarKeyguard() ? mStatusBar
+                    : lastFocusCanReceiveKeys ? mLastFocusedWindow
+                            : mTopFullscreenOpaqueWindowState;
+            if (winCandidate == null) {
+                return 0;
+            }
+        }
+        final WindowState win = winCandidate;
+        if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && isKeyguardOccluded()) {
+            // We are updating at a point where the keyguard has gotten
+            // focus, but we were last in a state where the top window is
+            // hiding it.  This is probably because the keyguard as been
+            // shown while the top window was displayed, so we want to ignore
+            // it here because this is just a very transient change and it
+            // will quickly lose focus once it correctly gets hidden.
+            return 0;
+        }
+
+        int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
+                & ~mResettingSystemUiFlags
+                & ~mForceClearedSystemUiFlags;
+        if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
+            tmpVisibility
+                    &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
+        }
+
+        final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
+                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
+        final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
+                mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
+        mService.getStackBounds(
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
+        mService.getStackBounds(
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
+        final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
+        final int diff = visibility ^ mLastSystemUiFlags;
+        final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
+        final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
+        final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
+        if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
+                && mFocusedApp == win.getAppToken()
+                && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
+                && mLastDockedStackBounds.equals(mDockedStackBounds)) {
+            return 0;
+        }
+        mLastSystemUiFlags = visibility;
+        mLastFullscreenStackSysUiFlags = fullscreenVisibility;
+        mLastDockedStackSysUiFlags = dockedVisibility;
+        mLastFocusNeedsMenu = needsMenu;
+        mFocusedApp = win.getAppToken();
+        final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
+        final Rect dockedStackBounds = new Rect(mDockedStackBounds);
+        mHandler.post(() -> {
+            StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+            if (statusBar != null) {
+                final int displayId = getDisplayId();
+                statusBar.setSystemUiVisibility(displayId, visibility, fullscreenVisibility,
+                        dockedVisibility, 0xffffffff, fullscreenStackBounds,
+                        dockedStackBounds, win.toString());
+                statusBar.topAppWindowChanged(displayId, needsMenu);
+            }
+        });
+        return diff;
+    }
+
+    private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
+        final boolean onKeyguard = isStatusBarKeyguard() && !isKeyguardOccluded();
+        final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming;
+        if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) {
+            // If the top fullscreen-or-dimming window is also the top fullscreen, respect
+            // its light flag.
+            vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+            vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
+                    & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        } else if (statusColorWin != null && statusColorWin.isDimming()) {
+            // Otherwise if it's dimming, clear the light flag.
+            vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        }
+        return vis;
+    }
+
+    @VisibleForTesting
+    @Nullable
+    static WindowState chooseNavigationColorWindowLw(WindowState opaque,
+            WindowState opaqueOrDimming, WindowState imeWindow,
+            @NavigationBarPosition int navBarPosition) {
+        // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME
+        // window can be navigation color window.
+        final boolean imeWindowCanNavColorWindow = imeWindow != null
+                && imeWindow.isVisibleLw()
+                && navBarPosition == NAV_BAR_BOTTOM
+                && (PolicyControl.getWindowFlags(imeWindow, null)
+                & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+
+        if (opaque != null && opaqueOrDimming == opaque) {
+            // If the top fullscreen-or-dimming window is also the top fullscreen, respect it
+            // unless IME window is also eligible, since currently the IME window is always show
+            // above the opaque fullscreen app window, regardless of the IME target window.
+            // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.
+            return imeWindowCanNavColorWindow ? imeWindow : opaque;
+        }
+
+        if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {
+            // No dimming window is involved. Determine the result only with the IME window.
+            return imeWindowCanNavColorWindow ? imeWindow : null;
+        }
+
+        if (!imeWindowCanNavColorWindow) {
+            // No IME window is involved. Determine the result only with opaqueOrDimming.
+            return opaqueOrDimming;
+        }
+
+        // The IME window and the dimming window are competing.  Check if the dimming window can be
+        // IME target or not.
+        if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) {
+            // The IME window is above the dimming window.
+            return imeWindow;
+        } else {
+            // The dimming window is above the IME window.
+            return opaqueOrDimming;
+        }
+    }
+
+    @VisibleForTesting
+    static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming,
+            WindowState imeWindow, WindowState navColorWin) {
+
+        if (navColorWin != null) {
+            if (navColorWin == imeWindow || navColorWin == opaque) {
+                // Respect the light flag.
+                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+                vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
+                        & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+            } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
+                // Clear the light flag for dimming window.
+                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+            }
+        }
+        return vis;
+    }
+
+    private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
+        final boolean dockedStackVisible =
+                mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final boolean freeformStackVisible =
+                mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM);
+        final boolean resizing = mDisplayContent.getDockedDividerController().isResizing();
+
+        // We need to force system bars when the docked stack is visible, when the freeform stack
+        // is visible but also when we are resizing for the transitions when docked stack
+        // visibility changes.
+        mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
+        final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
+
+        // apply translucent bar vis flags
+        WindowState fullscreenTransWin = isStatusBarKeyguard() && !isKeyguardOccluded()
+                ? mStatusBar
+                : mTopFullscreenOpaqueWindowState;
+        vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
+        vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
+        final int dockedVis = mStatusBarController.applyTranslucentFlagLw(
+                mTopDockedOpaqueWindowState, 0, 0);
+
+        final boolean fullscreenDrawsStatusBarBackground =
+                drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);
+        final boolean dockedDrawsStatusBarBackground =
+                drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);
+
+        // prevent status bar interaction from clearing certain flags
+        int type = win.getAttrs().type;
+        boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
+        if (statusBarHasFocus && !isStatusBarKeyguard()) {
+            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
+                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_IMMERSIVE
+                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                    | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+            if (isKeyguardOccluded()) {
+                flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
+            }
+            vis = (vis & ~flags) | (oldVis & flags);
+        }
+
+        if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
+            vis |= View.STATUS_BAR_TRANSPARENT;
+            vis &= ~View.STATUS_BAR_TRANSLUCENT;
+        } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)
+                || forceOpaqueStatusBar) {
+            vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
+        }
+
+        vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);
+
+        // update status bar
+        boolean immersiveSticky =
+                (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
+        final boolean hideStatusBarWM =
+                mTopFullscreenOpaqueWindowState != null
+                        && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
+                        & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
+        final boolean hideStatusBarSysui =
+                (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+        final boolean hideNavBarSysui =
+                (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
+
+        final boolean transientStatusBarAllowed = mStatusBar != null
+                && (statusBarHasFocus || (!mForceShowSystemBars
+                && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
+
+        final boolean transientNavBarAllowed = mNavigationBar != null
+                && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;
+
+        final long now = SystemClock.uptimeMillis();
+        final boolean pendingPanic = mPendingPanicGestureUptime != 0
+                && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
+        final DisplayPolicy defaultDisplayPolicy =
+                mService.getDefaultDisplayContentLocked().getDisplayPolicy();
+        if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard()
+                // TODO (b/111955725): Show keyguard presentation on all external displays
+                && defaultDisplayPolicy.isKeyguardDrawComplete()) {
+            // The user performed the panic gesture recently, we're about to hide the bars,
+            // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
+            mPendingPanicGestureUptime = 0;
+            mStatusBarController.showTransient();
+            if (!isNavBarEmpty(vis)) {
+                mNavigationBarController.showTransient();
+            }
+        }
+
+        final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
+                && !transientStatusBarAllowed && hideStatusBarSysui;
+        final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
+                && !transientNavBarAllowed;
+        if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {
+            // clear the clearable flags instead
+            clearClearableFlagsLw();
+            vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
+        }
+
+        final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
+        immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
+        final boolean navAllowedHidden = immersive || immersiveSticky;
+
+        if (hideNavBarSysui && !navAllowedHidden
+                && mService.mPolicy.getWindowLayerLw(win)
+                        > mService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) {
+            // We can't hide the navbar from this window otherwise the input consumer would not get
+            // the input events.
+            vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+        }
+
+        vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);
+
+        // update navigation bar
+        boolean oldImmersiveMode = isImmersiveMode(oldVis);
+        boolean newImmersiveMode = isImmersiveMode(vis);
+        if (oldImmersiveMode != newImmersiveMode) {
+            final String pkg = win.getOwningPackage();
+            mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
+                    mService.mPolicy.isUserSetupComplete(),
+                    isNavBarEmpty(win.getSystemUiVisibility()));
+        }
+
+        vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
+
+        final WindowState navColorWin = chooseNavigationColorWindowLw(
+                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
+                mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
+        vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
+                mTopFullscreenOpaqueOrDimmingWindowState,
+                mDisplayContent.mInputMethodWindow, navColorWin);
+
+        return vis;
+    }
+
+    private boolean drawsStatusBarBackground(int vis, WindowState win) {
+        if (!mStatusBarController.isTransparentAllowed(win)) {
+            return false;
+        }
+        if (win == null) {
+            return true;
+        }
+
+        final boolean drawsSystemBars =
+                (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+        final boolean forceDrawsSystemBars =
+                (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
+
+        return forceDrawsSystemBars || drawsSystemBars && (vis & View.STATUS_BAR_TRANSLUCENT) == 0;
+    }
+
+    /**
+     * @return the current visibility flags with the nav-bar opacity related flags toggled based
+     *         on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
+     */
+    private int configureNavBarOpacity(int visibility, boolean dockedStackVisible,
+            boolean freeformStackVisible, boolean isDockedDividerResizing) {
+        if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
+            if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) {
+                visibility = setNavBarOpaqueFlag(visibility);
+            }
+        } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
+            if (isDockedDividerResizing) {
+                visibility = setNavBarOpaqueFlag(visibility);
+            } else if (freeformStackVisible) {
+                visibility = setNavBarTranslucentFlag(visibility);
+            } else {
+                visibility = setNavBarOpaqueFlag(visibility);
+            }
+        }
+
+        if (!areTranslucentBarsAllowed()) {
+            visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT;
+        }
+        return visibility;
+    }
+
+    private int setNavBarOpaqueFlag(int visibility) {
+        return visibility & ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT);
+    }
+
+    private int setNavBarTranslucentFlag(int visibility) {
+        visibility &= ~View.NAVIGATION_BAR_TRANSPARENT;
+        return visibility | View.NAVIGATION_BAR_TRANSLUCENT;
+    }
+
+    private void clearClearableFlagsLw() {
+        int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS;
+        if (newVal != mResettingSystemUiFlags) {
+            mResettingSystemUiFlags = newVal;
+            mDisplayContent.reevaluateStatusBarVisibility();
+        }
+    }
+
+    private boolean isImmersiveMode(int vis) {
+        final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+        return mNavigationBar != null
+                && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
+                && (vis & flags) != 0
+                && canHideNavigationBar();
+    }
+
+    /**
+     * @return whether the navigation bar can be hidden, e.g. the device has a navigation bar
+     */
+    private boolean canHideNavigationBar() {
+        return hasNavigationBar();
+    }
+
+    private static boolean isNavBarEmpty(int systemUiFlags) {
+        final int disableNavigationBar = (View.STATUS_BAR_DISABLE_HOME
+                | View.STATUS_BAR_DISABLE_BACK
+                | View.STATUS_BAR_DISABLE_RECENT);
+
+        return (systemUiFlags & disableNavigationBar) == disableNavigationBar;
+    }
+
+    /**
+     * @return whether the navigation or status bar can be made translucent
+     *
+     * This should return true unless touch exploration is not enabled or
+     * R.boolean.config_enableTranslucentDecor is false.
+     */
+    private boolean areTranslucentBarsAllowed() {
+        return mTranslucentDecorEnabled;
+    }
+
+    boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation,
+            int newRotation) {
+        // For the upside down rotation we don't rotate seamlessly as the navigation
+        // bar moves position.
+        // Note most apps (using orientation:sensor or user as opposed to fullSensor)
+        // will not enter the reverse portrait orientation, so actually the
+        // orientation won't change at all.
+        if (oldRotation == displayRotation.getUpsideDownRotation()
+                || newRotation == displayRotation.getUpsideDownRotation()) {
+            return false;
+        }
+        // If the navigation bar can't change sides, then it will
+        // jump when we change orientations and we don't rotate
+        // seamlessly.
+        if (!navigationBarCanMove()) {
+            return false;
+        }
+
+        final WindowState w = mTopFullscreenOpaqueWindowState;
+        if (w != mFocusedWindow) {
+            return false;
+        }
+
+        // We only enable seamless rotation if the top window has requested
+        // it and is in the fullscreen opaque state. Seamless rotation
+        // requires freezing various Surface states and won't work well
+        // with animations, so we disable it in the animation case for now.
+        if (w != null && !w.isAnimatingLw()
+                && w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) {
+            return true;
+        }
+        return false;
+    }
+
+    private final Runnable mHiddenNavPanic = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                if (!mService.mPolicy.isUserSetupComplete()) {
+                    // Swipe-up for navigation bar is disabled during setup
+                    return;
+                }
+                mPendingPanicGestureUptime = SystemClock.uptimeMillis();
+                if (!isNavBarEmpty(mLastSystemUiFlags)) {
+                    mNavigationBarController.showTransient();
+                }
+            }
+        }
+    };
+
+    void onPowerKeyDown(boolean isScreenOn) {
+        // Detect user pressing the power button in panic when an application has
+        // taken over the whole screen.
+        boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn,
+                SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags),
+                isNavBarEmpty(mLastSystemUiFlags));
+        if (panic) {
+            mHandler.post(mHiddenNavPanic);
+        }
+    }
+
+    void onVrStateChangedLw(boolean enabled) {
+        mImmersiveModeConfirmation.onVrStateChangedLw(enabled);
+    }
+
+    /**
+     * Called when the state of lock task mode changes. This should be used to disable immersive
+     * mode confirmation.
+     *
+     * @param lockTaskState the new lock task mode state. One of
+     *                      {@link ActivityManager#LOCK_TASK_MODE_NONE},
+     *                      {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
+     *                      {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
+     */
+    public void onLockTaskStateChangedLw(int lockTaskState) {
+        mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState);
+    }
+
+    /**
+     * Request a screenshot be taken.
+     *
+     * @param screenshotType The type of screenshot, for example either
+     *                       {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or
+     *                       {@link WindowManager#TAKE_SCREENSHOT_SELECTED_REGION}
+     */
+    public void takeScreenshot(int screenshotType) {
+        if (mScreenshotHelper != null) {
+            mScreenshotHelper.takeScreenshot(screenshotType,
+                    mStatusBar != null && mStatusBar.isVisibleLw(),
+                    mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler);
+        }
+    }
+
     void dump(String prefix, PrintWriter pw) {
-        pw.println(prefix + "DisplayPolicy");
-        pw.print(prefix + "  mCarDockEnablesAccelerometer=" + mCarDockEnablesAccelerometer);
-        pw.println(" mDeskDockEnablesAccelerometer=" + mDeskDockEnablesAccelerometer);
-        pw.print(prefix + "  mDockMode=" + Intent.dockStateToString(mDockMode));
-        pw.println(" mLidState=" + WindowManagerFuncs.lidStateToString(mLidState));
-        pw.print(prefix + "  mAwake=" + mAwake);
-        pw.print(" mScreenOnEarly=" + mScreenOnEarly);
-        pw.println(" mScreenOnFully=" + mScreenOnFully);
-        pw.print(prefix + "  mKeyguardDrawComplete=" + mKeyguardDrawComplete);
-        pw.println(" mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
-        pw.println(prefix + "  mHdmiPlugged=" + mHdmiPlugged);
+        pw.print(prefix); pw.print("DisplayPolicy");
+        prefix += "  ";
+        pw.print(prefix);
+        pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer);
+        pw.print(" mDeskDockEnablesAccelerometer=");
+        pw.println(mDeskDockEnablesAccelerometer);
+        pw.print(prefix); pw.print("mDockMode="); pw.print(Intent.dockStateToString(mDockMode));
+        pw.print(" mLidState="); pw.println(WindowManagerFuncs.lidStateToString(mLidState));
+        pw.print(prefix); pw.print("mAwake="); pw.print(mAwake);
+        pw.print(" mScreenOnEarly="); pw.print(mScreenOnEarly);
+        pw.print(" mScreenOnFully="); pw.println(mScreenOnFully);
+        pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
+        pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
+        pw.print(prefix); pw.print("mHdmiPlugged="); pw.println(mHdmiPlugged);
+        if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0
+                || mForceClearedSystemUiFlags != 0) {
+            pw.print(prefix); pw.print("mLastSystemUiFlags=0x");
+            pw.print(Integer.toHexString(mLastSystemUiFlags));
+            pw.print(" mResettingSystemUiFlags=0x");
+            pw.print(Integer.toHexString(mResettingSystemUiFlags));
+            pw.print(" mForceClearedSystemUiFlags=0x");
+            pw.println(Integer.toHexString(mForceClearedSystemUiFlags));
+        }
+        if (mLastFocusNeedsMenu) {
+            pw.print(prefix); pw.print("mLastFocusNeedsMenu="); pw.println(mLastFocusNeedsMenu);
+        }
+        pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
+        pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
+        pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
+        if (mStatusBar != null) {
+            pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar);
+                    pw.print(" isStatusBarKeyguard="); pw.println(isStatusBarKeyguard());
+        }
+        if (mNavigationBar != null) {
+            pw.print(prefix); pw.print("mNavigationBar="); pw.println(mNavigationBar);
+        }
+        if (mFocusedWindow != null) {
+            pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow);
+        }
+        if (mFocusedApp != null) {
+            pw.print(prefix); pw.print("mFocusedApp="); pw.println(mFocusedApp);
+        }
+        if (mTopFullscreenOpaqueWindowState != null) {
+            pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState=");
+            pw.println(mTopFullscreenOpaqueWindowState);
+        }
+        if (mTopFullscreenOpaqueOrDimmingWindowState != null) {
+            pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState=");
+            pw.println(mTopFullscreenOpaqueOrDimmingWindowState);
+        }
+        if (mForcingShowNavBar) {
+            pw.print(prefix); pw.print("mForcingShowNavBar="); pw.println(mForcingShowNavBar);
+            pw.print(prefix); pw.print("mForcingShowNavBarLayer=");
+            pw.println(mForcingShowNavBarLayer);
+        }
+        pw.print(prefix); pw.print("mTopIsFullscreen="); pw.print(mTopIsFullscreen);
+        pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
+        pw.print(" mForceStatusBarFromKeyguard="); pw.println(mForceStatusBarFromKeyguard);
+        pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
+        mStatusBarController.dump(pw, prefix);
+        mNavigationBarController.dump(pw, prefix);
+
+        pw.print(prefix); pw.println("Looper state:");
+        mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + "  ");
     }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6ab7090..f1d1e49 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -844,9 +844,9 @@
         pw.print(prefix + "  mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
         pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
 
-        pw.print(prefix + "  mSupportAutoRotation=" + mSupportAutoRotation);
+        pw.println(prefix + "  mSupportAutoRotation=" + mSupportAutoRotation);
         if (mOrientationListener != null) {
-            pw.print(" mOrientationSensorEnabled=" + mOrientationListener.mEnabled);
+            mOrientationListener.dump(pw, prefix + "  ");
         }
         pw.println();
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index c377072..7ea88bb 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -28,6 +28,8 @@
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
 
 import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
 import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
@@ -52,6 +54,7 @@
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.LocalServices;
@@ -184,8 +187,8 @@
                     .calculateNonDismissingSnapTarget(position).position;
             DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect,
                     mTmpRect2.width(), mTmpRect2.height(), getContentWidth());
-            mService.mPolicy.getStableInsetsLw(rotation, mTmpRect2.width(), mTmpRect2.height(),
-                    displayCutout, mTmpRect3);
+            mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, mTmpRect2.width(),
+                    mTmpRect2.height(), displayCutout, mTmpRect3);
             mService.intersectDisplayInsetBounds(mTmpRect2, mTmpRect3, mTmpRect);
             minWidth = Math.min(mTmpRect.width(), minWidth);
         }
@@ -231,8 +234,9 @@
         final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
         final int displayWidth = parentConfig.windowConfiguration.getBounds().width();
         final int displayHeight = parentConfig.windowConfiguration.getBounds().height();
-        mService.mPolicy.getStableInsetsLw(parentConfig.windowConfiguration.getRotation(),
-                displayWidth, displayHeight, displayCutout, mTmpRect);
+        mDisplayContent.getDisplayPolicy().getStableInsetsLw(
+                parentConfig.windowConfiguration.getRotation(), displayWidth, displayHeight,
+                displayCutout, mTmpRect);
         int dividerSize = mDividerWindowWidth - 2 * mDividerInsets;
         // The offset in the left (landscape)/top (portrait) is calculated with the minimized
         // offset value with the divider size and any system insets in that direction.
@@ -240,7 +244,7 @@
             outBounds.set(0, mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top,
                     displayWidth, displayHeight);
         } else {
-            // In landscape also inset the left/right side with the statusbar height to match the
+            // In landscape also inset the left/right side with the status bar height to match the
             // minimized size height in portrait mode.
             final int primaryTaskWidth = mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top;
             int left = mTmpRect.left;
@@ -278,16 +282,16 @@
                     : mDisplayContent.mBaseDisplayHeight;
             final DisplayCutout displayCutout =
                     mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout();
-            mService.mPolicy.getStableInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
+            final DisplayPolicy displayPolicy =  mDisplayContent.getDisplayPolicy();
+            displayPolicy.getStableInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
             config.unset();
             config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
 
-            final int displayId = mDisplayContent.getDisplayId();
-            final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation,
-                baseConfig.uiMode, displayId, displayCutout);
-            final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
-                baseConfig.uiMode, displayId, displayCutout);
-            mService.mPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
+            final int appWidth = displayPolicy.getNonDecorDisplayWidth(dw, dh, rotation,
+                    baseConfig.uiMode, displayCutout);
+            final int appHeight = displayPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
+                    baseConfig.uiMode, displayCutout);
+            displayPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
             final int leftInset = mTmpRect.left;
             final int topInset = mTmpRect.top;
 
@@ -295,10 +299,10 @@
                     leftInset + appWidth /*right*/, topInset + appHeight /*bottom*/);
 
             final float density = mDisplayContent.getDisplayMetrics().density;
-            config.screenWidthDp = (int) (mService.mPolicy.getConfigDisplayWidth(dw, dh,
-                    rotation, baseConfig.uiMode, displayId, displayCutout) / density);
-            config.screenHeightDp = (int) (mService.mPolicy.getConfigDisplayHeight(dw, dh,
-                    rotation, baseConfig.uiMode, displayId, displayCutout) / density);
+            config.screenWidthDp = (int) (displayPolicy.getConfigDisplayWidth(dw, dh, rotation,
+                    baseConfig.uiMode, displayCutout) / density);
+            config.screenHeightDp = (int) (displayPolicy.getConfigDisplayHeight(dw, dh, rotation,
+                    baseConfig.uiMode, displayCutout) / density);
             final Context rotationContext = mService.mContext.createConfigurationContext(config);
             mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm(
                     rotationContext.getResources(), dw, dh, getContentWidth(),
@@ -464,8 +468,32 @@
      * @return true if the side provided is valid
      */
     boolean canPrimaryStackDockTo(int dockSide, Rect parentRect, int rotation) {
-        return mService.mPolicy.isDockSideAllowed(dockSide, mOriginalDockedSide,
-                parentRect.width(), parentRect.height(), rotation);
+        final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+        return isDockSideAllowed(dockSide, mOriginalDockedSide,
+                policy.navigationBarPosition(parentRect.width(), parentRect.height(), rotation),
+                policy.navigationBarCanMove());
+    }
+
+    @VisibleForTesting
+    static boolean isDockSideAllowed(int dockSide, int originalDockSide, int navBarPosition,
+            boolean navigationBarCanMove) {
+        if (dockSide == DOCKED_TOP) {
+            return true;
+        }
+
+        if (navigationBarCanMove) {
+            // Only allow the dockside opposite to the nav bar position in landscape
+            return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT
+                    || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT;
+        }
+
+        // Side is the same as original side
+        if (dockSide == originalDockSide) {
+            return true;
+        }
+
+        // Only if original docked side was top in portrait will allow left for landscape
+        return dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP;
     }
 
     void notifyDockedStackExistsChanged(boolean exists) {
diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
similarity index 81%
rename from services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
rename to services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 4aa2446..3d20501 100644
--- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.server.policy;
+package com.android.server.wm;
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
@@ -32,16 +33,14 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.service.vr.IVrManager;
-import android.service.vr.IVrStateCallbacks;
 import android.util.DisplayMetrics;
 import android.util.Slog;
+import android.view.Display;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
@@ -55,7 +54,6 @@
 import android.widget.FrameLayout;
 
 import com.android.internal.R;
-import com.android.server.vr.VrManagerService;
 
 /**
  *  Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden
@@ -67,30 +65,34 @@
     private static final boolean DEBUG_SHOW_EVERY_TIME = false; // super annoying, use with caution
     private static final String CONFIRMED = "confirmed";
 
+    private static boolean sConfirmed;
+
     private final Context mContext;
     private final H mHandler;
     private final long mShowDelayMs;
     private final long mPanicThresholdMs;
     private final IBinder mWindowToken = new Binder();
 
-    private boolean mConfirmed;
     private ClingWindowView mClingWindow;
     private long mPanicTime;
     private WindowManager mWindowManager;
-    private int mCurrentUserId;
     // Local copy of vr mode enabled state, to avoid calling into VrManager with
     // the lock held.
-    boolean mVrModeEnabled = false;
+    private boolean mVrModeEnabled;
     private int mLockTaskState = LOCK_TASK_MODE_NONE;
 
-    public ImmersiveModeConfirmation(Context context) {
-        mContext = ActivityThread.currentActivityThread().getSystemUiContext();
-        mHandler = new H();
+    ImmersiveModeConfirmation(Context context, Looper looper, boolean vrModeEnabled) {
+        final Display display = context.getDisplay();
+        final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+        mContext = display.getDisplayId() == DEFAULT_DISPLAY
+                ? uiContext : uiContext.createDisplayContext(display);
+        mHandler = new H(looper);
         mShowDelayMs = getNavBarExitDuration() * 3;
         mPanicThresholdMs = context.getResources()
                 .getInteger(R.integer.config_immersive_mode_confirmation_panic);
         mWindowManager = (WindowManager)
                 mContext.getSystemService(Context.WINDOW_SERVICE);
+        mVrModeEnabled = vrModeEnabled;
     }
 
     private long getNavBarExitDuration() {
@@ -98,57 +100,46 @@
         return exit != null ? exit.getDuration() : 0;
     }
 
-    public void loadSetting(int currentUserId) {
-        mConfirmed = false;
-        mCurrentUserId = currentUserId;
-        if (DEBUG) Slog.d(TAG, String.format("loadSetting() mCurrentUserId=%d", mCurrentUserId));
+    static boolean loadSetting(int currentUserId, Context context) {
+        final boolean wasConfirmed = sConfirmed;
+        sConfirmed = false;
+        if (DEBUG) Slog.d(TAG, String.format("loadSetting() currentUserId=%d", currentUserId));
         String value = null;
         try {
-            value = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+            value = Settings.Secure.getStringForUser(context.getContentResolver(),
                     Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
                     UserHandle.USER_CURRENT);
-            mConfirmed = CONFIRMED.equals(value);
-            if (DEBUG) Slog.d(TAG, "Loaded mConfirmed=" + mConfirmed);
+            sConfirmed = CONFIRMED.equals(value);
+            if (DEBUG) Slog.d(TAG, "Loaded sConfirmed=" + sConfirmed);
         } catch (Throwable t) {
             Slog.w(TAG, "Error loading confirmations, value=" + value, t);
         }
+        return sConfirmed != wasConfirmed;
     }
 
-    private void saveSetting() {
+    private static void saveSetting(Context context) {
         if (DEBUG) Slog.d(TAG, "saveSetting()");
         try {
-            final String value = mConfirmed ? CONFIRMED : null;
-            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+            final String value = sConfirmed ? CONFIRMED : null;
+            Settings.Secure.putStringForUser(context.getContentResolver(),
                     Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
                     value,
                     UserHandle.USER_CURRENT);
             if (DEBUG) Slog.d(TAG, "Saved value=" + value);
         } catch (Throwable t) {
-            Slog.w(TAG, "Error saving confirmations, mConfirmed=" + mConfirmed, t);
+            Slog.w(TAG, "Error saving confirmations, sConfirmed=" + sConfirmed, t);
         }
     }
 
-    void systemReady() {
-        IVrManager vrManager = IVrManager.Stub.asInterface(
-                ServiceManager.getService(Context.VR_SERVICE));
-        if (vrManager != null) {
-            try {
-                vrManager.registerListener(mVrStateCallbacks);
-                mVrModeEnabled = vrManager.getVrModeState();
-            } catch (RemoteException re) {
-            }
-        }
-    }
-
-    public void immersiveModeChangedLw(String pkg, boolean isImmersiveMode,
+    void immersiveModeChangedLw(String pkg, boolean isImmersiveMode,
             boolean userSetupComplete, boolean navBarEmpty) {
         mHandler.removeMessages(H.SHOW);
         if (isImmersiveMode) {
             final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg);
-            if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s mConfirmed=%s",
-                    disabled, mConfirmed));
+            if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s sConfirmed=%s",
+                    disabled, sConfirmed));
             if (!disabled
-                    && (DEBUG_SHOW_EVERY_TIME || !mConfirmed)
+                    && (DEBUG_SHOW_EVERY_TIME || !sConfirmed)
                     && userSetupComplete
                     && !mVrModeEnabled
                     && !navBarEmpty
@@ -161,7 +152,7 @@
         }
     }
 
-    public boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode,
+    boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode,
             boolean navBarEmpty) {
         if (!isScreenOn && (time - mPanicTime < mPanicThresholdMs)) {
             // turning the screen back on within the panic threshold
@@ -176,7 +167,7 @@
         return false;
     }
 
-    public void confirmCurrentPrompt() {
+    void confirmCurrentPrompt() {
         if (mClingWindow != null) {
             if (DEBUG) Slog.d(TAG, "confirmCurrentPrompt()");
             mHandler.post(mConfirm);
@@ -191,16 +182,14 @@
         }
     }
 
-    public WindowManager.LayoutParams getClingWindowLayoutParams() {
+    private WindowManager.LayoutParams getClingWindowLayoutParams() {
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
-                0
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                         | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                ,
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                 PixelFormat.TRANSLUCENT);
         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("ImmersiveModeConfirmation");
@@ -209,7 +198,7 @@
         return lp;
     }
 
-    public FrameLayout.LayoutParams getBubbleLayoutParams() {
+    private FrameLayout.LayoutParams getBubbleLayoutParams() {
         return new FrameLayout.LayoutParams(
                 mContext.getResources().getDimensionPixelSize(
                         R.dimen.immersive_mode_cling_width),
@@ -220,7 +209,7 @@
     /**
      * @return the window token that's used by all ImmersiveModeConfirmation windows.
      */
-    public IBinder getWindowToken() {
+    IBinder getWindowToken() {
         return mWindowToken;
     }
 
@@ -272,7 +261,7 @@
             }
         };
 
-        public ClingWindowView(Context context, Runnable confirm) {
+        ClingWindowView(Context context, Runnable confirm) {
             super(context);
             mConfirm = confirm;
             setBackground(mColor);
@@ -295,7 +284,7 @@
             mClingLayout = (ViewGroup)
                     View.inflate(getContext(), R.layout.immersive_mode_cling, null);
 
-            final Button ok = (Button) mClingLayout.findViewById(R.id.ok);
+            final Button ok = mClingLayout.findViewById(R.id.ok);
             ok.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
@@ -359,8 +348,7 @@
 
         // we will be hiding the nav bar, so layout as if it's already hidden
         mClingWindow.setSystemUiVisibility(
-                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
-              | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
 
         // show the confirmation
         WindowManager.LayoutParams lp = getClingWindowLayoutParams();
@@ -371,9 +359,9 @@
         @Override
         public void run() {
             if (DEBUG) Slog.d(TAG, "mConfirm.run()");
-            if (!mConfirmed) {
-                mConfirmed = true;
-                saveSetting();
+            if (!sConfirmed) {
+                sConfirmed = true;
+                saveSetting(mContext);
             }
             handleHide();
         }
@@ -383,6 +371,10 @@
         private static final int SHOW = 1;
         private static final int HIDE = 2;
 
+        H(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch(msg.what) {
@@ -396,16 +388,13 @@
         }
     }
 
-    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
-        @Override
-        public void onVrStateChanged(boolean enabled) throws RemoteException {
-            mVrModeEnabled = enabled;
-            if (mVrModeEnabled) {
-                mHandler.removeMessages(H.SHOW);
-                mHandler.sendEmptyMessage(H.HIDE);
-            }
+    void onVrStateChangedLw(boolean enabled) {
+        mVrModeEnabled = enabled;
+        if (mVrModeEnabled) {
+            mHandler.removeMessages(H.SHOW);
+            mHandler.sendEmptyMessage(H.HIDE);
         }
-    };
+    }
 
     void onLockTaskModeChangedLw(int lockTaskState) {
         mLockTaskState = lockTaskState;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 61bc4e4..83d32c8ad 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -40,14 +40,11 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
+import android.view.InputApplicationHandle;
 import android.view.InputChannel;
 import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.WindowManager;
-import android.view.InputApplicationHandle;
 import android.view.InputWindowHandle;
 
-import com.android.server.input.InputManagerService;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
@@ -401,10 +398,10 @@
             mTmpRect.setEmpty();
             mDisableWallpaperTouchEvents = false;
             this.inDrag = inDrag;
-            wallpaperController = mService.mRoot.mWallpaperController;
+            final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+            wallpaperController = dc.mWallpaperController;
 
-            mService.mRoot.getDisplayContent(mDisplayId).forAllWindows(this,
-                    true /* traverseTopToBottom */);
+            dc.forAllWindows(this, true /* traverseTopToBottom */);
             if (mAddWallpaperInputConsumerHandle) {
                 // No visible wallpaper found, add the wallpaper input consumer at the end.
                 addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
diff --git a/services/core/java/com/android/server/policy/NavigationBarExperiments.java b/services/core/java/com/android/server/wm/NavigationBarExperiments.java
similarity index 96%
rename from services/core/java/com/android/server/policy/NavigationBarExperiments.java
rename to services/core/java/com/android/server/wm/NavigationBarExperiments.java
index 06772e3..b74fb45 100644
--- a/services/core/java/com/android/server/policy/NavigationBarExperiments.java
+++ b/services/core/java/com/android/server/wm/NavigationBarExperiments.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.policy;
+package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -25,9 +25,6 @@
 import android.content.Context;
 import android.graphics.Rect;
 
-import com.android.server.policy.WindowManagerPolicy.WindowState;
-import com.android.server.wm.WindowFrames;
-
 /**
  * This class acts as a proxy for Navigation Bar experiments enabled with custom overlays
  * {@see OverlayManagerService}. By default with no overlays, this class will essentially do nothing
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index d21f67d9..ba23258 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -514,8 +514,9 @@
      */
     private void getInsetBounds(Rect outRect) {
         synchronized (mService.mGlobalLock) {
-            mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth,
-                    mDisplayInfo.logicalHeight, mDisplayInfo.displayCutout, mTmpInsets);
+            mDisplayContent.getDisplayPolicy().getStableInsetsLw(mDisplayInfo.rotation,
+                    mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight,
+                    mDisplayInfo.displayCutout, mTmpInsets);
             outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y,
                     mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x,
                     mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y);
diff --git a/services/core/java/com/android/server/policy/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java
similarity index 92%
rename from services/core/java/com/android/server/policy/PolicyControl.java
rename to services/core/java/com/android/server/wm/PolicyControl.java
index 48e72bc..4c8ce9e 100644
--- a/services/core/java/com/android/server/policy/PolicyControl.java
+++ b/services/core/java/com/android/server/wm/PolicyControl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.policy;
+package com.android.server.wm;
 
 import android.app.ActivityManager;
 import android.content.Context;
@@ -26,8 +26,6 @@
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 
-import com.android.server.policy.WindowManagerPolicy.WindowState;
-
 import java.io.PrintWriter;
 import java.io.StringWriter;
 
@@ -49,9 +47,9 @@
  * Separate multiple name-value pairs with ':'
  *   e.g. "immersive.status=apps:immersive.preconfirms=*"
  */
-public class PolicyControl {
-    private static String TAG = "PolicyControl";
-    private static boolean DEBUG = false;
+class PolicyControl {
+    private static final String TAG = "PolicyControl";
+    private static final boolean DEBUG = false;
 
     private static final String NAME_IMMERSIVE_FULL = "immersive.full";
     private static final String NAME_IMMERSIVE_STATUS = "immersive.status";
@@ -63,7 +61,7 @@
     private static Filter sImmersiveStatusFilter;
     private static Filter sImmersiveNavigationFilter;
 
-    public static int getSystemUiVisibility(WindowState win, LayoutParams attrs) {
+    static int getSystemUiVisibility(WindowState win, LayoutParams attrs) {
         attrs = attrs != null ? attrs : win.getAttrs();
         int vis = win != null ? win.getSystemUiVisibility()
                 : (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility);
@@ -84,7 +82,7 @@
         return vis;
     }
 
-    public static int getWindowFlags(WindowState win, LayoutParams attrs) {
+    static int getWindowFlags(WindowState win, LayoutParams attrs) {
         attrs = attrs != null ? attrs : win.getAttrs();
         int flags = attrs.flags;
         if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
@@ -98,7 +96,7 @@
         return flags;
     }
 
-    public static int adjustClearableFlags(WindowState win, int clearableFlags) {
+    static int adjustClearableFlags(WindowState win, int clearableFlags) {
         final LayoutParams attrs = win != null ? win.getAttrs() : null;
         if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
             clearableFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -106,28 +104,32 @@
         return clearableFlags;
     }
 
-    public static boolean disableImmersiveConfirmation(String pkg) {
+    static boolean disableImmersiveConfirmation(String pkg) {
         return (sImmersivePreconfirmationsFilter != null
                 && sImmersivePreconfirmationsFilter.matches(pkg))
                 || ActivityManager.isRunningInTestHarness();
     }
 
-    public static void reloadFromSetting(Context context) {
+    static boolean reloadFromSetting(Context context) {
         if (DEBUG) Slog.d(TAG, "reloadFromSetting()");
         String value = null;
         try {
             value = Settings.Global.getStringForUser(context.getContentResolver(),
                     Settings.Global.POLICY_CONTROL,
                     UserHandle.USER_CURRENT);
-            if (sSettingValue != null && sSettingValue.equals(value)) return;
+            if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) {
+                return false;
+            }
             setFilters(value);
             sSettingValue = value;
         } catch (Throwable t) {
             Slog.w(TAG, "Error loading policy control, value=" + value, t);
+            return false;
         }
+        return true;
     }
 
-    public static void dump(String prefix, PrintWriter pw) {
+    static void dump(String prefix, PrintWriter pw) {
         dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw);
         dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw);
         dump("sImmersivePreconfirmationsFilter", sImmersivePreconfirmationsFilter, prefix, pw);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2399716..b483fd3 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -51,7 +51,6 @@
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
@@ -106,7 +105,6 @@
     private boolean mSustainedPerformanceModeEnabled = false;
     private boolean mSustainedPerformanceModeCurrent = false;
 
-    boolean mWallpaperMayChange = false;
     // During an orientation change, we track whether all windows have rendered
     // at the new orientation, and this will be false from changing orientation until that occurs.
     // For seamless rotation cases this always stays true, as the windows complete their orientation
@@ -114,8 +112,6 @@
     boolean mOrientationChangeComplete = true;
     boolean mWallpaperActionPending = false;
 
-    final WallpaperController mWallpaperController;
-
     private final Handler mHandler;
 
     private String mCloseSystemDialogsReason;
@@ -124,13 +120,10 @@
     // events.
     int mTopFocusedDisplayId = INVALID_DISPLAY;
 
-    // Only a seperate transaction until we seperate the apply surface changes
+    // Only a separate transaction until we separate the apply surface changes
     // transaction from the global transaction.
     private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction();
 
-    private final Consumer<DisplayContent> mDisplayContentConfigChangesConsumer =
-            mService.mPolicy::onConfigurationChanged;
-
     private final Consumer<WindowState> mCloseSystemDialogsConsumer = w -> {
         if (w.mHasSurface) {
             try {
@@ -150,7 +143,6 @@
     RootWindowContainer(WindowManagerService service) {
         super(service);
         mHandler = new MyHandler(service.mH.getLooper());
-        mWallpaperController = new WallpaperController(mService);
     }
 
     boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -236,8 +228,7 @@
             return existing;
         }
 
-        final DisplayContent dc =
-                new DisplayContent(display, mService, mWallpaperController, controller);
+        final DisplayContent dc = new DisplayContent(display, mService, controller);
 
         if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
 
@@ -368,8 +359,6 @@
     public void onConfigurationChanged(Configuration newParentConfig) {
         prepareFreezingTaskBounds();
         super.onConfigurationChanged(newParentConfig);
-
-        forAllDisplays(mDisplayContentConfigChangesConsumer);
     }
 
     private void prepareFreezingTaskBounds() {
@@ -579,14 +568,19 @@
         final RecentsAnimationController recentsAnimationController =
                 mService.getRecentsAnimationController();
         if (recentsAnimationController != null) {
-            recentsAnimationController.checkAnimationReady(mWallpaperController);
+            recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
         }
 
-        if (mWallpaperMayChange) {
-            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change!  Adjusting");
-            defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-            if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
-                    defaultDisplay.pendingLayoutChanges);
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mChildren.get(displayNdx);
+            if (displayContent.mWallpaperMayChange) {
+                if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change!  Adjusting");
+                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+                if (DEBUG_LAYOUT_REPEATS) {
+                    surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
+                            displayContent.pendingLayoutChanges);
+                }
+            }
         }
 
         if (mService.mFocusMayChange) {
@@ -617,7 +611,6 @@
         }
 
         // Destroy the surface of any windows that are no longer visible.
-        boolean wallpaperDestroyed = false;
         i = mService.mDestroySurface.size();
         if (i > 0) {
             do {
@@ -629,7 +622,7 @@
                     displayContent.setInputMethodWindowLocked(null);
                 }
                 if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
-                    wallpaperDestroyed = true;
+                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                 }
                 win.destroySurfaceUnchecked();
                 win.mWinAnimator.destroyPreservedSurfaceLocked();
@@ -643,11 +636,6 @@
             displayContent.removeExistingTokensIfPossible();
         }
 
-        if (wallpaperDestroyed) {
-            defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-            defaultDisplay.setLayoutNeeded();
-        }
-
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
             if (displayContent.pendingLayoutChanges != 0) {
@@ -905,10 +893,6 @@
             mUpdateRotation = true;
             doRequest = true;
         }
-        if ((bulkUpdateParams & SET_WALLPAPER_MAY_CHANGE) != 0) {
-            mWallpaperMayChange = true;
-            doRequest = true;
-        }
         if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
             mOrientationChangeComplete = false;
         } else {
@@ -1063,6 +1047,12 @@
         }
     }
 
+    void forAllDisplayPolicies(Consumer<DisplayPolicy> callback) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            callback.accept(mChildren.get(i).getDisplayPolicy());
+        }
+    }
+
     /**
      * Get current topmost focused IME window in system.
      * Will look on all displays in current Z-order.
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b411fad..6838c55 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -29,7 +29,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.content.ClipData;
-import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Binder;
@@ -42,7 +41,6 @@
 import android.os.UserHandle;
 import android.util.MergedConfiguration;
 import android.util.Slog;
-import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowId;
@@ -60,6 +58,7 @@
 import java.io.PrintWriter;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.function.BiConsumer;
 
 /**
  * This class represents an active client session.  There is generally one
@@ -315,14 +314,19 @@
         }
     }
 
+    private void actionOnWallpaper(IBinder window,
+            BiConsumer<WallpaperController, WindowState> action) {
+        final WindowState windowState = mService.windowForClientLocked(this, window, true);
+        action.accept(windowState.getDisplayContent().mWallpaperController, windowState);
+    }
+
     @Override
     public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
         synchronized (mService.mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
-                mService.mRoot.mWallpaperController.setWindowWallpaperPosition(
-                        mService.windowForClientLocked(this, window, true),
-                        x, y, xStep, yStep);
+                actionOnWallpaper(window, (wpController, windowState) ->
+                        wpController.setWindowWallpaperPosition(windowState, x, y, xStep, yStep));
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -332,7 +336,8 @@
     @Override
     public void wallpaperOffsetsComplete(IBinder window) {
         synchronized (mService.mGlobalLock) {
-            mService.mRoot.mWallpaperController.wallpaperOffsetsComplete(window);
+            actionOnWallpaper(window, (wpController, windowState) ->
+                    wpController.wallpaperOffsetsComplete(window));
         }
     }
 
@@ -341,8 +346,8 @@
         synchronized (mService.mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
-                mService.mRoot.mWallpaperController.setWindowWallpaperDisplayOffset(
-                        mService.windowForClientLocked(this, window, true), x, y);
+                actionOnWallpaper(window, (wpController, windowState) ->
+                        wpController.setWindowWallpaperDisplayOffset(windowState, x, y));
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -355,9 +360,9 @@
         synchronized (mService.mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
-                return mService.mRoot.mWallpaperController.sendWindowWallpaperCommand(
-                        mService.windowForClientLocked(this, window, true),
-                        action, x, y, z, extras, sync);
+                final WindowState windowState = mService.windowForClientLocked(this, window, true);
+                return windowState.getDisplayContent().mWallpaperController
+                        .sendWindowWallpaperCommand(windowState, action, x, y, z, extras, sync);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -367,7 +372,8 @@
     @Override
     public void wallpaperCommandComplete(IBinder window, Bundle result) {
         synchronized (mService.mGlobalLock) {
-            mService.mRoot.mWallpaperController.wallpaperCommandComplete(window);
+            actionOnWallpaper(window, (wpController, windowState) ->
+                    wpController.wallpaperCommandComplete(window));
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index baeedbc..35264a2 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -261,11 +261,12 @@
             final DisplayContent displayContent = stack.getDisplayContent();
             final DisplayInfo di = displayContent.getDisplayInfo();
             final DisplayCutout displayCutout = di.displayCutout;
+            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
 
             // Get the insets and display bounds
-            mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+            displayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
                     displayCutout, mTmpStableInsets);
-            mService.mPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+            displayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
                     displayCutout, mTmpNonDecorInsets);
             mTmpDisplayBounds.set(0, 0, di.logicalWidth, di.logicalHeight);
 
diff --git a/services/core/java/com/android/server/wm/StatusBarController.java b/services/core/java/com/android/server/wm/StatusBarController.java
new file mode 100644
index 0000000..b4de75b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/StatusBarController.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
+
+import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+
+import android.app.StatusBarManager;
+import android.os.IBinder;
+import android.view.View;
+
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+/**
+ * Implements status bar specific behavior.
+ */
+public class StatusBarController extends BarController {
+
+    private final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
+
+        private Runnable mAppTransitionPending = () -> {
+            StatusBarManagerInternal statusBar = getStatusBarInternal();
+            if (statusBar != null && mWin != null) {
+                statusBar.appTransitionPending(mWin.getDisplayId());
+            }
+        };
+
+        private Runnable mAppTransitionCancelled = () -> {
+            StatusBarManagerInternal statusBar = getStatusBarInternal();
+            if (statusBar != null && mWin != null) {
+                statusBar.appTransitionCancelled(mWin.getDisplayId());
+            }
+        };
+
+        private Runnable mAppTransitionFinished = () -> {
+            StatusBarManagerInternal statusBar = getStatusBarInternal();
+            if (statusBar != null && mWin != null) {
+                statusBar.appTransitionFinished(mWin.getDisplayId());
+            }
+        };
+
+        @Override
+        public void onAppTransitionPendingLocked() {
+            mHandler.post(mAppTransitionPending);
+        }
+
+        @Override
+        public int onAppTransitionStartingLocked(int transit, IBinder openToken,
+                IBinder closeToken, long duration, long statusBarAnimationStartTime,
+                long statusBarAnimationDuration) {
+            mHandler.post(() -> {
+                StatusBarManagerInternal statusBar = getStatusBarInternal();
+                if (statusBar != null && mWin != null) {
+                    statusBar.appTransitionStarting(mWin.getDisplayId(),
+                            statusBarAnimationStartTime, statusBarAnimationDuration);
+                }
+            });
+            return 0;
+        }
+
+        @Override
+        public void onAppTransitionCancelledLocked(int transit) {
+            mHandler.post(mAppTransitionCancelled);
+        }
+
+        @Override
+        public void onAppTransitionFinishedLocked(IBinder token) {
+            mHandler.post(mAppTransitionFinished);
+        }
+    };
+
+    StatusBarController() {
+        super("StatusBar",
+                View.STATUS_BAR_TRANSIENT,
+                View.STATUS_BAR_UNHIDE,
+                View.STATUS_BAR_TRANSLUCENT,
+                StatusBarManager.WINDOW_STATUS_BAR,
+                FLAG_TRANSLUCENT_STATUS,
+                View.STATUS_BAR_TRANSPARENT);
+    }
+
+    void setTopAppHidesStatusBar(boolean hidesStatusBar) {
+        StatusBarManagerInternal statusBar = getStatusBarInternal();
+        if (statusBar != null) {
+            statusBar.setTopAppHidesStatusBar(hidesStatusBar);
+        }
+    }
+
+    @Override
+    protected boolean skipAnimation() {
+        return mWin.getAttrs().height == MATCH_PARENT;
+    }
+
+    AppTransitionListener getAppTransitionListener() {
+        return mAppTransitionListener;
+    }
+}
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
similarity index 93%
rename from services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
rename to services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
index d3cc8ef..bdb76c2 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.server.policy;
+package com.android.server.wm;
 
 import android.content.Context;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.SystemClock;
 import android.util.Slog;
 import android.view.GestureDetector;
@@ -27,11 +26,11 @@
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 import android.widget.OverScroller;
 
-/*
+/**
  * Listens for system-wide input gestures, firing callbacks when detected.
  * @hide
  */
-public class SystemGesturesPointerEventListener implements PointerEventListener {
+class SystemGesturesPointerEventListener implements PointerEventListener {
     private static final String TAG = "SystemGestures";
     private static final boolean DEBUG = false;
     private static final long SWIPE_TIMEOUT_MS = 500;
@@ -46,6 +45,7 @@
     private static final int SWIPE_FROM_LEFT = 4;
 
     private final Context mContext;
+    private final Handler mHandler;
     private final int mSwipeStartThreshold;
     private final int mSwipeDistanceThreshold;
     private final Callbacks mCallbacks;
@@ -55,7 +55,6 @@
     private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];
 
     private GestureDetector mGestureDetector;
-    private OverScroller mOverscroller;
 
     int screenHeight;
     int screenWidth;
@@ -65,8 +64,9 @@
     private boolean mMouseHoveringAtEdge;
     private long mLastFlingTime;
 
-    public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
+    SystemGesturesPointerEventListener(Context context, Handler handler, Callbacks callbacks) {
         mContext = context;
+        mHandler = handler;
         mCallbacks = checkNull("callbacks", callbacks);
         mSwipeStartThreshold = checkNull("context", context).getResources()
                 .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
@@ -83,9 +83,7 @@
     }
 
     public void systemReady() {
-        Handler h = new Handler(Looper.myLooper());
-        mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h);
-        mOverscroller = new OverScroller(mContext);
+        mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler);
     }
 
     @Override
@@ -163,14 +161,14 @@
     private void captureDown(MotionEvent event, int pointerIndex) {
         final int pointerId = event.getPointerId(pointerIndex);
         final int i = findIndex(pointerId);
-        if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
-                " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
+        if (DEBUG) Slog.d(TAG, "pointer " + pointerId
+                + " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
         if (i != UNTRACKED_POINTER) {
             mDownX[i] = event.getX(pointerIndex);
             mDownY[i] = event.getY(pointerIndex);
             mDownTime[i] = event.getEventTime();
-            if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
-                    " down x=" + mDownX[i] + " y=" + mDownY[i]);
+            if (DEBUG) Slog.d(TAG, "pointer " + pointerId
+                    + " down x=" + mDownX[i] + " y=" + mDownY[i]);
         }
     }
 
@@ -242,6 +240,13 @@
     }
 
     private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
+
+        private OverScroller mOverscroller;
+
+        FlingGestureDetector() {
+            mOverscroller = new OverScroller(mContext);
+        }
+
         @Override
         public boolean onSingleTapUp(MotionEvent e) {
             if (!mOverscroller.isFinished()) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 15de1ec..64f4ba5 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -521,7 +521,7 @@
         // Snap the position to a target.
         final int rotation = parentConfig.windowConfiguration.getRotation();
         final int orientation = parentConfig.orientation;
-        mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight,
+        mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight,
                 displayCutout, outBounds);
         final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
                 mService.mContext.getResources(), displayWidth, displayHeight,
@@ -867,7 +867,8 @@
             // and its bounds can be adjusted after that. The bounds of all other stacks are
             // adjusted to occupy whatever screen space the docked stack isn't occupying.
             final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
-            mService.mPolicy.getStableInsetsLw(parentConfig.windowConfiguration.getRotation(),
+            mDisplayContent.getDisplayPolicy().getStableInsetsLw(
+                    parentConfig.windowConfiguration.getRotation(),
                     displayRect.width(), displayRect.height(), displayCutout, mTmpRect2);
             final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
                     displayRect.width(),
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 29e1b20..15239c7 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -60,6 +60,7 @@
 class WallpaperController {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM;
     private WindowManagerService mService;
+    private final DisplayContent mDisplayContent;
 
     private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
 
@@ -187,8 +188,9 @@
         return false;
     };
 
-    public WallpaperController(WindowManagerService service) {
+    WallpaperController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
+        mDisplayContent = displayContent;
     }
 
     WindowState getWallpaperTarget() {
@@ -397,11 +399,7 @@
     }
 
     private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
-        final DisplayContent displayContent = changingTarget.getDisplayContent();
-        if (displayContent == null) {
-            return;
-        }
-        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
 
@@ -464,15 +462,15 @@
         }
     }
 
-    private void findWallpaperTarget(DisplayContent dc) {
+    private void findWallpaperTarget() {
         mFindResults.reset();
-        if (dc.isStackVisible(WINDOWING_MODE_FREEFORM)) {
+        if (mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM)) {
             // In freeform mode we set the wallpaper as its own target, so we don't need an
             // additional window to make it visible.
             mFindResults.setUseTopWallpaperAsTarget(true);
         }
 
-        dc.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
+        mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
 
         if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) {
             mFindResults.setWallpaperTarget(mFindResults.topWallpaper);
@@ -485,8 +483,7 @@
     }
 
     /** Updates the target wallpaper if needed and returns true if an update happened. */
-    private void updateWallpaperWindowsTarget(DisplayContent dc,
-            FindWallpaperTargetResult result) {
+    private void updateWallpaperWindowsTarget(FindWallpaperTargetResult result) {
 
         WindowState wallpaperTarget = result.wallpaperTarget;
 
@@ -529,7 +526,7 @@
             return;
         }
 
-        if (dc.getWindow(w -> w == prevWallpaperTarget) == null) {
+        if (mDisplayContent.getWindow(w -> w == prevWallpaperTarget) == null) {
             return;
         }
 
@@ -550,9 +547,9 @@
             // is not. If they're both hidden, still use the new target.
             mWallpaperTarget = prevWallpaperTarget;
         } else if (newTargetHidden == oldTargetHidden
-                && !dc.mOpeningApps.contains(wallpaperTarget.mAppToken)
-                && (dc.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
-                || dc.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
+                && !mDisplayContent.mOpeningApps.contains(wallpaperTarget.mAppToken)
+                && (mDisplayContent.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
+                || mDisplayContent.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
             // If they're both hidden (or both not hidden), prefer the one that's currently in
             // opening or closing app list, this allows transition selection logic to better
             // determine the wallpaper status of opening/closing apps.
@@ -570,18 +567,21 @@
         }
     }
 
-    void adjustWallpaperWindows(DisplayContent dc) {
-        mService.mRoot.mWallpaperMayChange = false;
+    void adjustWallpaperWindows() {
+        mDisplayContent.mWallpaperMayChange = false;
 
         // First find top-most window that has asked to be on top of the wallpaper;
         // all wallpapers go behind it.
-        findWallpaperTarget(dc);
-        updateWallpaperWindowsTarget(dc, mFindResults);
+        findWallpaperTarget();
+        updateWallpaperWindowsTarget(mFindResults);
 
         // The window is visible to the compositor...but is it visible to the user?
         // That is what the wallpaper cares about.
         final boolean visible = mWallpaperTarget != null && isWallpaperVisible(mWallpaperTarget);
-        if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);
+        if (DEBUG_WALLPAPER) {
+            Slog.v(TAG, "Wallpaper visibility: " + visible + " at display "
+                    + mDisplayContent.getDisplayId());
+        }
 
         if (visible) {
             if (mWallpaperTarget.mWallpaperX >= 0) {
@@ -637,9 +637,11 @@
                 }
                 if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
                     mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
-                    mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
-                    mService.mH.sendEmptyMessageDelayed(WALLPAPER_DRAW_PENDING_TIMEOUT,
-                            WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+                    mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this);
+                    mService.mH.sendMessageDelayed(
+                                mService.mH.obtainMessage(WALLPAPER_DRAW_PENDING_TIMEOUT, this),
+                                WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+
                 }
                 if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
                         "Wallpaper should be visible but has not been drawn yet. " +
@@ -649,7 +651,7 @@
         }
         if (wallpaperReady) {
             mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
-            mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
+            mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this);
         }
 
         return transitionReady;
@@ -659,10 +661,9 @@
      * Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
      * the opening apps should be a wallpaper target.
      */
-    void adjustWallpaperWindowsForAppTransitionIfNeeded(DisplayContent dc,
-            ArraySet<AppWindowToken> openingApps) {
+    void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<AppWindowToken> openingApps) {
         boolean adjust = false;
-        if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+        if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
             adjust = true;
         } else {
             for (int i = openingApps.size() - 1; i >= 0; --i) {
@@ -675,7 +676,7 @@
         }
 
         if (adjust) {
-            adjustWallpaperWindows(dc);
+            adjustWallpaperWindows();
         }
     }
 
@@ -740,6 +741,7 @@
     }
 
     void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId());
         pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
         if (mPrevWallpaperTarget != null) {
             pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 46bb981..449c409 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -270,9 +271,6 @@
         if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
             builder.append(" UPDATE_ROTATION");
         }
-        if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE) != 0) {
-            builder.append(" WALLPAPER_MAY_CHANGE");
-        }
         if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
             builder.append(" ORIENTATION_CHANGE_COMPLETE");
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index b096bf2..e83b863 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -392,11 +392,6 @@
     public abstract boolean isStackVisible(int windowingMode);
 
     /**
-     * @return True if and only if the docked divider is currently in resize mode.
-     */
-    public abstract boolean isDockedDividerResizing();
-
-    /**
      * Requests the window manager to resend the windows for accessibility.
      */
     public abstract void computeWindowsForAccessibility();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c47b22f..39a8465 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -171,6 +171,8 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -231,6 +233,7 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.LatencyTracker;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.view.WindowManagerPolicyThread;
 import com.android.server.AnimationThread;
 import com.android.server.DisplayThread;
@@ -373,6 +376,18 @@
     boolean mKeyguardOrAodShowingOnDefaultDisplay;
     // VR Vr2d Display Id.
     int mVr2dDisplayId = INVALID_DISPLAY;
+    boolean mVrModeEnabled = false;
+
+    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+        @Override
+        public void onVrStateChanged(boolean enabled) {
+            synchronized (mGlobalLock) {
+                mVrModeEnabled = enabled;
+                mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
+                        DisplayPolicy::onVrStateChangedLw, PooledLambda.__(), enabled));
+            }
+        }
+    };
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -528,6 +543,7 @@
     boolean mForceDisplayEnabled = false;
     boolean mShowingBootMessages = false;
     boolean mBootAnimationStopped = false;
+    boolean mSystemReady = false;
 
     // Following variables are for debugging screen wakelock only.
     WindowState mLastWakeLockHoldingWindow = null;
@@ -579,9 +595,6 @@
     final WallpaperVisibilityListeners mWallpaperVisibilityListeners =
             new WallpaperVisibilityListeners();
 
-    int mSystemDecorLayer = 0;
-    final Rect mScreenRect = new Rect();
-
     boolean mDisplayFrozen = false;
     long mDisplayFreezeTime = 0;
     int mLastDisplayFreezeDuration = 0;
@@ -597,11 +610,6 @@
     boolean mClientFreezingScreen = false;
     int mAppsFreezingScreen = 0;
 
-    // Last systemUiVisibility we received from status bar.
-    int mLastStatusBarVisibility = 0;
-    // Last systemUiVisibility we dispatched to windows.
-    int mLastDispatchedSystemUiVisibility = 0;
-
     // State while inside of layoutAndPlaceSurfacesLocked().
     boolean mFocusMayChange;
 
@@ -649,6 +657,10 @@
                 Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE);
         private final Uri mAnimationDurationScaleUri =
                 Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE);
+        private final Uri mImmersiveModeConfirmationsUri =
+                Settings.Secure.getUriFor(Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS);
+        private final Uri mPolicyControlUri =
+                Settings.Global.getUriFor(Settings.Global.POLICY_CONTROL);
 
         public SettingsObserver() {
             super(new Handler());
@@ -661,6 +673,10 @@
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(mAnimationDurationScaleUri, false, this,
                     UserHandle.USER_ALL);
+            resolver.registerContentObserver(mImmersiveModeConfirmationsUri, false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(mPolicyControlUri, false, this,
+                    UserHandle.USER_ALL);
         }
 
         @Override
@@ -669,23 +685,40 @@
                 return;
             }
 
+            if (mImmersiveModeConfirmationsUri.equals(uri) || mPolicyControlUri.equals(uri)) {
+                updateSystemUiSettings();
+                return;
+            }
+
             if (mDisplayInversionEnabledUri.equals(uri)) {
                 updateCircularDisplayMaskIfNeeded();
+                return;
+            }
+
+            @UpdateAnimationScaleMode
+            final int mode;
+            if (mWindowAnimationScaleUri.equals(uri)) {
+                mode = WINDOW_ANIMATION_SCALE;
+            } else if (mTransitionAnimationScaleUri.equals(uri)) {
+                mode = TRANSITION_ANIMATION_SCALE;
+            } else if (mAnimationDurationScaleUri.equals(uri)) {
+                mode = ANIMATION_DURATION_SCALE;
             } else {
-                @UpdateAnimationScaleMode
-                final int mode;
-                if (mWindowAnimationScaleUri.equals(uri)) {
-                    mode = WINDOW_ANIMATION_SCALE;
-                } else if (mTransitionAnimationScaleUri.equals(uri)) {
-                    mode = TRANSITION_ANIMATION_SCALE;
-                } else if (mAnimationDurationScaleUri.equals(uri)) {
-                    mode = ANIMATION_DURATION_SCALE;
-                } else {
-                    // Ignoring unrecognized content changes
-                    return;
-                }
-                Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0);
-                mH.sendMessage(m);
+                // Ignoring unrecognized content changes
+                return;
+            }
+            Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0);
+            mH.sendMessage(m);
+        }
+
+        void updateSystemUiSettings() {
+            boolean changed;
+            synchronized (mWindowMap) {
+                changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext)
+                        || PolicyControl.reloadFromSetting(mContext);
+            }
+            if (changed) {
+                updateRotation(false /* alwaysSendConfiguration */, false /* forceRelayout */);
             }
         }
     }
@@ -1286,10 +1319,11 @@
             final boolean hasStatusBarServicePermission =
                     mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
                             == PackageManager.PERMISSION_GRANTED;
-            mPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);
+            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
+            displayPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);
             win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
 
-            res = mPolicy.prepareAddWindowLw(win, attrs);
+            res = displayPolicy.prepareAddWindowLw(win, attrs);
             if (res != WindowManagerGlobal.ADD_OKAY) {
                 return res;
             }
@@ -1422,9 +1456,8 @@
                 taskBounds = null;
                 floatingStack = false;
             }
-            if (mPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack,
-                    outFrame, outContentInsets, outStableInsets, outOutsets,
-                    outDisplayCutout)) {
+            if (displayPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack,
+                    outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
             }
 
@@ -1842,6 +1875,8 @@
                 return 0;
             }
             displayId = win.getDisplayId();
+            final DisplayContent displayContent = win.getDisplayContent();
+            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
 
             WindowStateAnimator winAnimator = win.mWinAnimator;
             if (viewVisibility != View.GONE) {
@@ -1857,7 +1892,7 @@
             int attrChanges = 0;
             int flagChanges = 0;
             if (attrs != null) {
-                mPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);
+                displayPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);
                 // if they don't have the permission, mask out the status bar bits
                 if (seq == win.mSeq) {
                     int systemUiVisibility = attrs.systemUiVisibility
@@ -1996,7 +2031,7 @@
                 try {
                     result = createSurfaceControl(outSurface, result, win, winAnimator);
                 } catch (Exception e) {
-                    win.getDisplayContent().getInputMonitor().updateInputWindowsLw(true /*force*/);
+                    displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
 
                     Slog.w(TAG_WM, "Exception thrown when creating surface for client "
                              + client + " (" + win.mAttrs.getTitle() + ")",
@@ -2007,7 +2042,6 @@
                 if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                     focusMayChange = true;
                 }
-                final DisplayContent displayContent = win.getDisplayContent();
                 if (win.mAttrs.type == TYPE_INPUT_METHOD
                         && displayContent.mInputMethodWindow == null) {
                     displayContent.setInputMethodWindowLocked(win);
@@ -2052,35 +2086,34 @@
             // updateFocusedWindowLocked() already assigned layers so we only need to
             // reassign them at this point if the IM window state gets shuffled
             boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
-            final DisplayContent dc = win.getDisplayContent();
             if (imMayMove) {
-                dc.computeImeTarget(true /* updateImeTarget */);
+                displayContent.computeImeTarget(true /* updateImeTarget */);
                 if (toBeDisplayed) {
                     // Little hack here -- we -should- be able to rely on the function to return
                     // true if the IME has moved and needs its layer recomputed. However, if the IME
                     // was hidden and isn't actually moved in the list, its layer may be out of data
                     // so we make sure to recompute it.
-                    dc.assignWindowLayers(false /* setLayoutNeeded */);
+                    displayContent.assignWindowLayers(false /* setLayoutNeeded */);
                 }
             }
 
             if (wallpaperMayMove) {
-                win.getDisplayContent().pendingLayoutChanges |=
+                displayContent.pendingLayoutChanges |=
                         WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
             }
 
             if (win.mAppToken != null) {
-                dc.mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
+                displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
             }
 
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                     "relayoutWindow: updateOrientationFromAppTokens");
-            configChanged = dc.updateOrientationFromAppTokens();
+            configChanged = displayContent.updateOrientationFromAppTokens();
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
             if (toBeDisplayed && win.mIsWallpaper) {
-                DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo();
-                dc.mWallpaperController.updateWallpaperOffset(
+                DisplayInfo displayInfo = displayContent.getDisplayInfo();
+                displayContent.mWallpaperController.updateWallpaperOffset(
                         win, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
             }
             if (win.mAppToken != null) {
@@ -2090,7 +2123,7 @@
                 winAnimator.mReportSurfaceResized = false;
                 result |= WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED;
             }
-            if (mPolicy.isNavBarForcedShownLw(win)) {
+            if (displayPolicy.isNavBarForcedShownLw(win)) {
                 result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR;
             }
             if (!win.isGoneForLayoutLw()) {
@@ -2286,7 +2319,7 @@
         }
 
         synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContent(displayId);
+            final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
             if (dc == null) {
                 Slog.w(TAG_WM, "addWindowToken: Attempted to add token: " + binder
                         + " for non-exiting displayId=" + displayId);
@@ -2408,7 +2441,7 @@
                 mWaitingForConfig = true;
                 displayContent.setLayoutNeeded();
                 int anim[] = new int[2];
-                mPolicy.selectRotationAnimationLw(anim);
+                displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
 
                 startFreezingDisplayLocked(anim[0], anim[1], displayContent);
                 config = new Configuration(mTempConfiguration);
@@ -2603,7 +2636,9 @@
         }
     }
 
-    @Override
+    /**
+     * Notifies window manager that {@link DisplayPolicy#isShowingDreamLw} has changed.
+     */
     public void notifyShowingDreamChanged() {
         // TODO(multi-display): support show dream in multi-display.
         notifyKeyguardFlagsChanged(null /* callback */, DEFAULT_DISPLAY);
@@ -2634,6 +2669,21 @@
         mH.sendEmptyMessage(H.RECOMPUTE_FOCUS);
     }
 
+    @Override
+    public void onPowerKeyDown(boolean isScreenOn) {
+        mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
+                DisplayPolicy::onPowerKeyDown, PooledLambda.__(), isScreenOn));
+    }
+
+    @Override
+    public void onUserSwitched() {
+        mSettingsObserver.updateSystemUiSettings();
+        synchronized (mWindowMap) {
+            // force a re-application of focused window sysui visibility on each display.
+            mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemUiVisibilityLw);
+        }
+    }
+
     /**
      * Starts deferring layout passes. Useful when doing multiple changes but to optimize
      * performance, only one layout pass should be done. This can be called multiple times, and
@@ -2839,7 +2889,8 @@
 
     public boolean isShowingDream() {
         synchronized (mGlobalLock) {
-            return mPolicy.isShowingDreamLw();
+            // TODO: fix this when dream can be shown on non-default display.
+            return getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw();
         }
     }
 
@@ -3462,7 +3513,9 @@
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
             synchronized (mGlobalLock) {
-                return mRoot.mWallpaperController.screenshotWallpaperLocked();
+                // TODO(b/115486823) Screenshot at secondary displays if needed.
+                final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY);
+                return dc.mWallpaperController.screenshotWallpaperLocked();
             }
         } finally {
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3670,13 +3723,6 @@
     }
 
     @Override
-    public WindowManagerPolicy.DisplayContentInfo getDefaultDisplayContentInfo() {
-        synchronized (mGlobalLock) {
-            return getDefaultDisplayContentLocked();
-        }
-    }
-
-    @Override
     public int getDefaultDisplayRotation() {
         synchronized (mGlobalLock) {
             return getDefaultDisplayContentLocked().getRotation();
@@ -4289,10 +4335,29 @@
     }
 
     public void systemReady() {
+        mSystemReady = true;
         mPolicy.systemReady();
+        mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady);
         mTaskSnapshotController.systemReady();
         mHasWideColorGamutSupport = queryWideColorGamutSupport();
         mHasHdrSupport = queryHdrSupport();
+        UiThread.getHandler().post(mSettingsObserver::updateSystemUiSettings);
+        IVrManager vrManager = IVrManager.Stub.asInterface(
+                ServiceManager.getService(Context.VR_SERVICE));
+        if (vrManager != null) {
+            try {
+                final boolean vrModeEnabled = vrManager.getVrModeState();
+                synchronized (mGlobalLock) {
+                    vrManager.registerListener(mVrStateCallbacks);
+                    if (vrModeEnabled) {
+                        mVrModeEnabled = vrModeEnabled;
+                        mVrStateCallbacks.onVrStateChanged(vrModeEnabled);
+                    }
+                }
+            } catch (RemoteException e) {
+                // Ignore, we cannot do anything if we failed to register VR mode listener
+            }
+        }
     }
 
     private static boolean queryWideColorGamutSupport() {
@@ -4694,7 +4759,10 @@
                 break;
                 case WALLPAPER_DRAW_PENDING_TIMEOUT: {
                     synchronized (mGlobalLock) {
-                        if (mRoot.mWallpaperController.processWallpaperDrawPendingTimeout()) {
+                        final WallpaperController wallpaperController =
+                                (WallpaperController) msg.obj;
+                        if (wallpaperController != null
+                                && wallpaperController.processWallpaperDrawPendingTimeout()) {
                             mWindowPlacerLocked.performSurfacePlacement();
                         }
                     }
@@ -5377,7 +5445,8 @@
             if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation");
             DisplayInfo displayInfo = displayContent.getDisplayInfo();
             // Get rotation animation again, with new top window
-            if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
+            if (!displayContent.getDisplayPolicy()
+                    .validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
                 mExitAnimId = mEnterAnimId = 0;
             }
             if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
@@ -5517,7 +5586,7 @@
     }
 
     @Override
-    public void statusBarVisibilityChanged(int visibility) {
+    public void statusBarVisibilityChanged(int displayId, int visibility) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Caller does not hold permission "
@@ -5525,9 +5594,12 @@
         }
 
         synchronized (mGlobalLock) {
-            mLastStatusBarVisibility = visibility;
-            visibility = mPolicy.adjustSystemUiVisibilityLw(visibility);
-            updateStatusBarVisibilityLocked(visibility);
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent != null) {
+                displayContent.statusBarVisibilityChanged(visibility);
+            } else {
+                Slog.w(TAG, "statusBarVisibilityChanged with invalid displayId=" + displayId);
+            }
         }
     }
 
@@ -5543,49 +5615,36 @@
         }
     }
 
-    // TODO(multidisplay): StatusBar on multiple screens?
-    private boolean updateStatusBarVisibilityLocked(int visibility) {
-        if (mLastDispatchedSystemUiVisibility == visibility) {
-            return false;
-        }
-        final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility)
-                // We are only interested in differences of one of the
-                // clearable flags...
-                & View.SYSTEM_UI_CLEARABLE_FLAGS
-                // ...if it has actually been cleared.
-                & ~visibility;
-
-        mLastDispatchedSystemUiVisibility = visibility;
-        mInputManager.setSystemUiVisibility(visibility);
-        getDefaultDisplayContentLocked().updateSystemUiVisibility(visibility, globalDiff);
-        return true;
-    }
-
+    // TODO: Make the callers use getNavBarPosition(int) only.
+    /**
+     * Used by SystemUI and shared SystemUI libraries.
+     * @see DisplayPolicy#getNavBarPosition()
+     */
     @Override
-    public void reevaluateStatusBarVisibility() {
-        synchronized (mGlobalLock) {
-            int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
-            if (updateStatusBarVisibilityLocked(visibility)) {
-                mWindowPlacerLocked.requestTraversal();
-            }
-        }
+    @WindowManagerPolicy.NavigationBarPosition
+    public int getNavBarPosition() {
+        return getNavBarPosition(Display.DEFAULT_DISPLAY);
     }
 
     /**
      * Used by ActivityManager to determine where to position an app with aspect ratio shorter then
      * the screen is.
-     * @see WindowManagerPolicy#getNavBarPosition()
+     * @see DisplayPolicy#getNavBarPosition()
      */
-    @Override
     @WindowManagerPolicy.NavigationBarPosition
-    public int getNavBarPosition() {
+    public int getNavBarPosition(int displayId) {
         synchronized (mGlobalLock) {
             // Perform layout if it was scheduled before to make sure that we get correct nav bar
             // position when doing rotations.
-            final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
-            defaultDisplayContent.performLayout(false /* initial */,
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent == null) {
+                Slog.w(TAG, "getNavBarPosition with invalid displayId=" + displayId
+                        + " callers=" + Debug.getCallers(3));
+                return -1;
+            }
+            displayContent.performLayout(false /* initial */,
                     false /* updateInputWindows */);
-            return mPolicy.getNavBarPosition();
+            return displayContent.getDisplayPolicy().getNavBarPosition();
         }
     }
 
@@ -5771,11 +5830,6 @@
         }
     }
 
-    @Override
-    public int getDockedDividerInsetsLw() {
-        return getDefaultDisplayContentLocked().getDockedDividerController().getContentInsets();
-    }
-
     private void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) {
         pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
         mPolicy.dump("    ", pw, args);
@@ -5964,18 +6018,11 @@
         mTaskSnapshotController.dump(pw, "  ");
 
         if (dumpAll) {
-            pw.print("  mSystemDecorLayer="); pw.print(mSystemDecorLayer);
-                    pw.print(" mScreenRect="); pw.println(mScreenRect.toShortString());
-            if (mLastStatusBarVisibility != 0) {
-                pw.print("  mLastStatusBarVisibility=0x");
-                        pw.println(Integer.toHexString(mLastStatusBarVisibility));
-            }
             final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
             if (imeWindow != null) {
                 pw.print("  mInputMethodWindow="); pw.println(imeWindow);
             }
             mWindowPlacerLocked.dump(pw, "  ");
-            mRoot.mWallpaperController.dump(pw, "  ");
             pw.print("  mSystemBooted="); pw.print(mSystemBooted);
                     pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
 
@@ -6003,6 +6050,7 @@
                 pw.print("  mRecentsAnimationController="); pw.println(mRecentsAnimationController);
                 mRecentsAnimationController.dump(pw, "    ");
             }
+            PolicyControl.dump("  ", pw);
         }
     }
 
@@ -6266,7 +6314,7 @@
     public void onOverlayChanged() {
         synchronized (mGlobalLock) {
             mRoot.forAllDisplays(displayContent -> {
-                mPolicy.onOverlayChangedLw(displayContent);
+                displayContent.getDisplayPolicy().onOverlayChangedLw();
                 displayContent.updateDisplayInfo();
             });
             requestTraversal();
@@ -6496,7 +6544,7 @@
         final DisplayContent dc = mRoot.getDisplayContent(displayId);
         if (dc != null) {
             final DisplayInfo di = dc.getDisplayInfo();
-            mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+            dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
                     di.displayCutout, outInsets);
         }
     }
@@ -7098,13 +7146,6 @@
         }
 
         @Override
-        public boolean isDockedDividerResizing() {
-            synchronized (mGlobalLock) {
-                return getDefaultDisplayContentLocked().getDockedDividerController().isResizing();
-            }
-        }
-
-        @Override
         public void computeWindowsForAccessibility() {
             final AccessibilityController accessibilityController;
             synchronized (mGlobalLock) {
@@ -7363,9 +7404,11 @@
      *                      {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
      *                      {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
      */
-    public void onLockTaskStateChanged(int lockTaskState) {
+    void onLockTaskStateChanged(int lockTaskState) {
+        // TODO: pass in displayId to determine which display the lock task state changed
         synchronized (mGlobalLock) {
-            mPolicy.onLockTaskStateChangedLw(lockTaskState);
+            mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
+                    DisplayPolicy::onLockTaskStateChangedLw, PooledLambda.__(), lockTaskState));
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a117cf3..567b583 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -179,6 +179,7 @@
 import android.view.InputChannel;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
+import android.view.InputWindowHandle;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
@@ -191,7 +192,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
-import android.view.InputWindowHandle;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 import com.android.server.wm.utils.InsetUtils;
@@ -1800,7 +1800,7 @@
             // also been registered in display.
             dc.mTapExcludeProvidingWindows.remove(this);
         }
-        mPolicy.removeWindowLw(this);
+        dc.getDisplayPolicy().removeWindowLw(this);
 
         disposeInputChannel();
 
@@ -2982,7 +2982,7 @@
 
         mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
                 reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
-                mPolicy.isNavBarForcedShownLw(this), displayId,
+                getDisplayContent().getDisplayPolicy().isNavBarForcedShownLw(this), displayId,
                 new DisplayCutout.ParcelableWrapper(displayCutout));
         mDragResizingChangeReported = true;
     }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 838d2a1..e090cc5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -245,7 +245,7 @@
         mSession = win.mSession;
         mAttrType = win.mAttrs.type;
         mIsWallpaper = win.mIsWallpaper;
-        mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
+        mWallpaperControllerLocked = win.getDisplayContent().mWallpaperController;
     }
 
     void cancelExitAnimationForNextAnimationLocked() {
@@ -1343,7 +1343,7 @@
         // is running.
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");
         if (mWin.mToken.okToAnimate()) {
-            int anim = mPolicy.selectAnimationLw(mWin, transit);
+            int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimationLw(mWin, transit);
             int attr = -1;
             Animation a = null;
             if (anim != 0) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7d25b8c..7193dd7 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -38,7 +38,6 @@
 class WindowSurfacePlacer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfacePlacer" : TAG_WM;
     private final WindowManagerService mService;
-    private final WallpaperController mWallpaperControllerLocked;
 
     private boolean mInLayout = false;
 
@@ -46,7 +45,6 @@
     private int mLayoutRepeatCount;
 
     static final int SET_UPDATE_ROTATION                = 1 << 0;
-    static final int SET_WALLPAPER_MAY_CHANGE           = 1 << 1;
     static final int SET_ORIENTATION_CHANGE_COMPLETE    = 1 << 2;
     static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 3;
 
@@ -59,7 +57,6 @@
 
     public WindowSurfacePlacer(WindowManagerService service) {
         mService = service;
-        mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
         mPerformSurfacePlacement = () -> {
             synchronized (mService.mGlobalLock) {
                 performSurfacePlacement();
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 04a526f..bf83ac13 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -83,6 +83,7 @@
         "libui",
         "libinput",
         "libinputflinger",
+        "libinputflinger_base",
         "libinputservice",
         "libschedulerservicehidl",
         "libsensorservice",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e2db807..ee8a08b 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -341,8 +341,7 @@
     }
     mInteractive = true;
 
-    sp<EventHub> eventHub = new EventHub();
-    mInputManager = new InputManager(eventHub, this, this);
+    mInputManager = new InputManager(this, this);
 }
 
 NativeInputManager::~NativeInputManager() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 58c4bbf..d798865 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -494,7 +494,8 @@
         pkg.usesLibraryFiles = new String[] { "foo13"};
 
         pkg.usesLibraryInfos = new ArrayList<>();
-        pkg.usesLibraryInfos.add(new SharedLibraryInfo(null, null, null, 0L, 0, null, null, null));
+        pkg.usesLibraryInfos.add(
+                new SharedLibraryInfo(null, null, null, null, 0L, 0, null, null, null));
 
         pkg.mOriginalPackages = new ArrayList<>();
         pkg.mOriginalPackages.add("foo14");
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index cd15a57..813fa82 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.fail;
 
 import android.content.pm.ApplicationInfo;
+import android.content.pm.SharedLibraryInfo;
 import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
@@ -39,6 +40,7 @@
 
 import java.io.File;
 import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -108,22 +110,31 @@
         return data;
     }
 
+    private List<SharedLibraryInfo> createMockSharedLibrary(String [] sharedLibrary) {
+        SharedLibraryInfo info = new SharedLibraryInfo(null, null, Arrays.asList(sharedLibrary),
+                null, 0L, SharedLibraryInfo.TYPE_STATIC, null, null, null);
+        ArrayList<SharedLibraryInfo> libraries = new ArrayList<>();
+        libraries.add(info);
+        return libraries;
+    }
+
     @Test
     public void testSplitChain() {
         TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
-        String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+        List<SharedLibraryInfo> sharedLibrary =
+                createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
                 data.info, sharedLibrary, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
-        assertEquals("PCL[a.dex:b.dex]", contexts[0]);
-        assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]",
+        assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
+        assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}",
                 contexts[1]);
-        assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
-        assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
-        assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
-        assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
-        assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+        assertEquals("DLC[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[2]);
+        assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[3]);
+        assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[4]);
+        assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[5]);
+        assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[6]);
         assertEquals(null, contexts[7]);  // config split
         assertEquals("PCL[]", contexts[8]);  // feature split with no dependency
     }
@@ -131,25 +142,28 @@
     @Test
     public void testSplitChainNoSplitDependencies() {
         TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
-        String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+        List<SharedLibraryInfo> sharedLibrary =
+                createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
                 data.info, sharedLibrary, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
-        assertEquals("PCL[a.dex:b.dex]", contexts[0]);
-        assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]);
-        assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]);
-        assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex]", contexts[3]);
-        assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex]", contexts[4]);
+        assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
+        assertEquals("PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[1]);
+        assertEquals("PCL[base.dex:base-1.dex]{PCL[a.dex:b.dex]}", contexts[2]);
+        assertEquals("PCL[base.dex:base-1.dex:base-2.dex]{PCL[a.dex:b.dex]}", contexts[3]);
         assertEquals(
-                "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]",
+                "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex]{PCL[a.dex:b.dex]}",
+                contexts[4]);
+        assertEquals(
+                "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]{PCL[a.dex:b.dex]}",
                 contexts[5]);
         assertEquals(
-                "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
+                "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]{PCL[a.dex:b.dex]}",
                 contexts[6]);
         assertEquals(null, contexts[7]);  // config split
         assertEquals(
-                "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
+                "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]{PCL[a.dex:b.dex]}",
                 contexts[8]);  // feature split with no dependency
     }
 
@@ -200,18 +214,21 @@
     public void testSplitChainWithNullPrimaryClassLoader() {
         // A null classLoaderName should mean PathClassLoader.
         TestData data = createMockApplicationInfo(null, true, true);
-        String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+        List<SharedLibraryInfo> sharedLibrary =
+                createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
                 data.info, sharedLibrary, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
-        assertEquals("PCL[a.dex:b.dex]", contexts[0]);
-        assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
-        assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
-        assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
-        assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
-        assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
-        assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+        assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
+        assertEquals(
+                "DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}",
+                contexts[1]);
+        assertEquals("DLC[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[2]);
+        assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[3]);
+        assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[4]);
+        assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[5]);
+        assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[6]);
         assertEquals(null, contexts[7]);  // config split
         assertEquals("PCL[]", contexts[8]);  // feature split with no dependency
     }
@@ -219,35 +236,38 @@
     @Test
     public void tesNoSplits() {
         TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
-        String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+        List<SharedLibraryInfo> sharedLibrary =
+                createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
                 data.info, sharedLibrary, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
-        assertEquals("PCL[a.dex:b.dex]", contexts[0]);
+        assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
     }
 
     @Test
     public void tesNoSplitsNullClassLoaderName() {
         TestData data = createMockApplicationInfo(null, false, false);
-        String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+        List<SharedLibraryInfo> sharedLibrary =
+                createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
                 data.info, sharedLibrary, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
-        assertEquals("PCL[a.dex:b.dex]", contexts[0]);
+        assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
     }
 
     @Test
     public void tesNoSplitDelegateLast() {
         TestData data = createMockApplicationInfo(
                 DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
-        String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+        List<SharedLibraryInfo> sharedLibrary =
+                createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
                 data.info, sharedLibrary, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
-        assertEquals("DLC[a.dex:b.dex]", contexts[0]);
+        assertEquals("DLC[]{PCL[a.dex:b.dex]}", contexts[0]);
     }
 
     @Test
@@ -276,7 +296,8 @@
         TestData data = createMockApplicationInfo(null, true, false);
         Arrays.fill(data.pathsWithCode, false);
 
-        String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+        List<SharedLibraryInfo> sharedLibrary =
+                createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
                 data.info, sharedLibrary, data.pathsWithCode);
 
@@ -295,18 +316,21 @@
     public void testContextBaseNoCode() {
         TestData data = createMockApplicationInfo(null, true, true);
         data.pathsWithCode[0] = false;
-        String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+        List<SharedLibraryInfo> sharedLibrary =
+                createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
                 data.info, sharedLibrary, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
         assertEquals(null, contexts[0]);
-        assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
-        assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
-        assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
-        assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
-        assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
-        assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+        assertEquals(
+                "DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}",
+                contexts[1]);
+        assertEquals("DLC[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[2]);
+        assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[3]);
+        assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[4]);
+        assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[5]);
+        assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[6]);
         assertEquals(null, contexts[7]);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
new file mode 100644
index 0000000..7cf7df13
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.IThermalEventListener;
+import android.os.IThermalStatusListener;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.Temperature;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.SystemService;
+import com.android.server.power.ThermalManagerService.ThermalHalWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server
+ * /power/ThermalManagerServiceTest.java
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ThermalManagerServiceTest {
+    private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
+    private ThermalManagerService mService;
+    private ThermalHalFake mFakeHal;
+    private PowerManager mPowerManager;
+    @Mock
+    private Context mContext;
+    @Mock
+    private IPowerManager mIPowerManagerMock;
+    @Mock
+    private IThermalEventListener mEventListener1;
+    @Mock
+    private IThermalEventListener mEventListener2;
+    @Mock
+    private IThermalStatusListener mStatusListener1;
+    @Mock
+    private IThermalStatusListener mStatusListener2;
+
+    /**
+     * Fake Hal class.
+     */
+    private class ThermalHalFake extends ThermalHalWrapper {
+        private static final int INIT_STATUS = Temperature.THROTTLING_NONE;
+        private ArrayList<Temperature> mTemperatureList = new ArrayList<>();
+        private Temperature mSkin1 = new Temperature(0, Temperature.TYPE_SKIN, "skin1",
+                INIT_STATUS);
+        private Temperature mSkin2 = new Temperature(0, Temperature.TYPE_SKIN, "skin2",
+                INIT_STATUS);
+        private Temperature mBattery = new Temperature(0, Temperature.TYPE_BATTERY, "batt",
+                INIT_STATUS);
+        private Temperature mUsbPort = new Temperature(0, Temperature.TYPE_USB_PORT, "usbport",
+                INIT_STATUS);
+
+        ThermalHalFake() {
+            mTemperatureList.add(mSkin1);
+            mTemperatureList.add(mSkin2);
+            mTemperatureList.add(mBattery);
+            mTemperatureList.add(mUsbPort);
+        }
+
+        @Override
+        protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) {
+            return mTemperatureList;
+        }
+
+        @Override
+        protected boolean connectToHal() {
+            return true;
+        }
+
+        @Override
+        protected void dump(PrintWriter pw, String prefix) {
+            return;
+        }
+    }
+
+    private void assertTemperatureEquals(List<Temperature> expected, List<Temperature> value) {
+        assertEquals(new HashSet<>(expected), new HashSet<>(value));
+    }
+
+    @Before
+    public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+        mFakeHal = new ThermalHalFake();
+        mPowerManager = new PowerManager(mContext, mIPowerManagerMock, null);
+        when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
+        when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
+        resetListenerMock();
+        mService = new ThermalManagerService(mContext, mFakeHal);
+        // Register callbacks before AMS ready and no callback sent
+        assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+        assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+        assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+                Temperature.TYPE_SKIN));
+        assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).notifyThrottling(any(Temperature.class));
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).notifyThrottling(any(Temperature.class));
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+        ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(4)).notifyThrottling(captor.capture());
+        assertTemperatureEquals(mFakeHal.mTemperatureList, captor.getAllValues());
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+        captor = ArgumentCaptor.forClass(Temperature.class);
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(2)).notifyThrottling(captor.capture());
+        assertTemperatureEquals(new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)),
+                captor.getAllValues());
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+    }
+
+    private void resetListenerMock() {
+        reset(mEventListener1);
+        reset(mStatusListener1);
+        reset(mEventListener2);
+        reset(mStatusListener2);
+        doReturn(mock(IBinder.class)).when(mEventListener1).asBinder();
+        doReturn(mock(IBinder.class)).when(mStatusListener1).asBinder();
+        doReturn(mock(IBinder.class)).when(mEventListener2).asBinder();
+        doReturn(mock(IBinder.class)).when(mStatusListener2).asBinder();
+    }
+
+    @Test
+    public void testRegister() throws RemoteException {
+        // Unregister all
+        assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
+        assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
+        assertTrue(mService.mService.unregisterThermalEventListener(mEventListener2));
+        assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener2));
+        resetListenerMock();
+        // Register callbacks and verify they are called
+        assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+        assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+        ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(4)).notifyThrottling(captor.capture());
+        assertTemperatureEquals(mFakeHal.mTemperatureList, captor.getAllValues());
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+        // Register new callbacks and verify old ones are not called (remained same) while new
+        // ones are called
+        assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+                Temperature.TYPE_SKIN));
+        assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(4)).notifyThrottling(any(Temperature.class));
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+        captor = ArgumentCaptor.forClass(Temperature.class);
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(2)).notifyThrottling(captor.capture());
+        assertTemperatureEquals(new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)),
+                captor.getAllValues());
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+    }
+
+    @Test
+    public void testNotify() throws RemoteException {
+        int status = Temperature.THROTTLING_SEVERE;
+        Temperature newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
+        mFakeHal.mCallback.onValues(newBattery);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newBattery);
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(status);
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).notifyThrottling(newBattery);
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(status);
+        resetListenerMock();
+        // Should only notify event not status
+        Temperature newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status);
+        mFakeHal.mCallback.onValues(newSkin);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newSkin);
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newSkin);
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        resetListenerMock();
+        // Back to None, should only notify event not status
+        status = Temperature.THROTTLING_NONE;
+        newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
+        mFakeHal.mCallback.onValues(newBattery);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newBattery);
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).notifyThrottling(newBattery);
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        resetListenerMock();
+        // Should also notify status
+        newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status);
+        mFakeHal.mCallback.onValues(newSkin);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newSkin);
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(status);
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newSkin);
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(status);
+    }
+
+    @Test
+    public void testGetCurrentTemperatures() throws RemoteException {
+        assertTemperatureEquals(mFakeHal.getCurrentTemperatures(false, 0),
+                mService.mService.getCurrentTemperatures());
+        assertTemperatureEquals(mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN),
+                mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN));
+    }
+
+    @Test
+    public void testGetCurrentStatus() throws RemoteException {
+        int status = Temperature.THROTTLING_WARNING;
+        Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
+        mFakeHal.mCallback.onValues(newSkin);
+        assertEquals(status, mService.mService.getCurrentStatus());
+    }
+
+    @Test
+    public void testThermalShutdown() throws RemoteException {
+        int status = Temperature.THROTTLING_SHUTDOWN;
+        Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
+        mFakeHal.mCallback.onValues(newSkin);
+        verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
+    }
+
+    @Test
+    public void testNoHal() throws RemoteException {
+        mService = new ThermalManagerService(mContext);
+        // Do no call onActivityManagerReady to skip connect HAL
+        assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+        assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+        assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
+        assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
+        assertEquals(0, mService.mService.getCurrentTemperatures().size());
+        assertEquals(0,
+                mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
+        assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentStatus());
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
similarity index 71%
rename from services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
rename to services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index f024fe7..18bd2e4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.policy;
+package com.android.server.wm;
 
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
@@ -25,36 +25,27 @@
 
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-import android.view.Display;
 import android.view.DisplayInfo;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ErrorCollector;
+import org.junit.runner.RunWith;
 
-/**
- * Build/Install/Run:
- *  atest WmTests:PhoneWindowManagerInsetsTest
- */
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
-public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase {
+public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
 
     @Rule
     public final ErrorCollector mErrorCollector = new ErrorCollector();
 
-    @Before
-    public void setUp() throws Exception {
-        addStatusBar();
-        addNavigationBar();
-    }
-
     @Test
     public void portrait() {
-        DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */);
+        final DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */);
 
         verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
         verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
@@ -63,7 +54,7 @@
 
     @Test
     public void portrait_withCutout() {
-        DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */);
+        final DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */);
 
         verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
         verifyNonDecorInsets(di, 0, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT);
@@ -72,7 +63,7 @@
 
     @Test
     public void landscape() {
-        DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */);
+        final DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */);
 
         verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
         verifyNonDecorInsets(di, 0, 0, NAV_BAR_HEIGHT, 0);
@@ -81,7 +72,7 @@
 
     @Test
     public void landscape_withCutout() {
-        DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */);
+        final DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */);
 
         verifyStableInsets(di, DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
         verifyNonDecorInsets(di, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT, 0);
@@ -90,7 +81,7 @@
 
     @Test
     public void seascape() {
-        DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */);
+        final DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */);
 
         verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
         verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, 0, 0);
@@ -99,7 +90,7 @@
 
     @Test
     public void seascape_withCutout() {
-        DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */);
+        final DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */);
 
         verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
         verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, DISPLAY_CUTOUT_HEIGHT, 0);
@@ -108,7 +99,7 @@
 
     @Test
     public void upsideDown() {
-        DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */);
+        final DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */);
 
         verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
         verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
@@ -117,7 +108,7 @@
 
     @Test
     public void upsideDown_withCutout() {
-        DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */);
+        final DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */);
 
         verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
         verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
@@ -151,35 +142,39 @@
 
     private Rect getStableInsetsLw(DisplayInfo di) {
         Rect result = new Rect();
-        mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+        mDisplayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
                 di.displayCutout, result);
         return result;
     }
 
     private Rect getNonDecorInsetsLw(DisplayInfo di) {
         Rect result = new Rect();
-        mPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+        mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
                 di.displayCutout, result);
         return result;
     }
 
     private int getNonDecorDisplayWidth(DisplayInfo di) {
-        return mPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight, di.rotation,
-                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+        return mDisplayPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight,
+                di.rotation, 0 /* ui */, di.displayCutout);
     }
 
     private int getNonDecorDisplayHeight(DisplayInfo di) {
-        return mPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight, di.rotation,
-                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+        return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight,
+                di.rotation, 0 /* ui */, di.displayCutout);
     }
 
     private int getConfigDisplayWidth(DisplayInfo di) {
-        return mPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight, di.rotation,
-                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+        return mDisplayPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight,
+                di.rotation, 0 /* ui */, di.displayCutout);
     }
 
     private int getConfigDisplayHeight(DisplayInfo di) {
-        return mPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight, di.rotation,
-                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+        return mDisplayPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight,
+                di.rotation, 0 /* ui */, di.displayCutout);
+    }
+
+    private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
+        return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
new file mode 100644
index 0000000..a91c5e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.utils.WmDisplayCutout;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
+
+    private DisplayFrames mFrames;
+    private WindowState mWindow;
+    private int mRotation = ROTATION_0;
+    private boolean mHasDisplayCutout;
+
+    @Before
+    public void setUp() throws Exception {
+        updateDisplayFrames();
+
+        mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
+        // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
+        // changing those frames.
+        doNothing().when(mWindow).computeFrameLw();
+
+        final WindowManager.LayoutParams attrs = mWindow.mAttrs;
+        attrs.width = MATCH_PARENT;
+        attrs.height = MATCH_PARENT;
+        attrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        attrs.format = PixelFormat.TRANSLUCENT;
+    }
+
+    public void setRotation(int rotation) {
+        mRotation = rotation;
+        updateDisplayFrames();
+    }
+
+    public void addDisplayCutout() {
+        mHasDisplayCutout = true;
+        updateDisplayFrames();
+    }
+
+    private void updateDisplayFrames() {
+        final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
+                mHasDisplayCutout);
+        mFrames = new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second);
+    }
+
+    @Test
+    public void layoutWindowLw_appDrawsBars() {
+        mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_appWontDrawBars() {
+        mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+    }
+
+    @Test
+    public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception {
+        mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+    }
+
+    @Test
+    public void addingWindow_doesNotTamperWithSysuiFlags() {
+        mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        addWindow(mWindow);
+
+        assertEquals(0, mWindow.mAttrs.systemUiVisibility);
+        assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout() {
+        addDisplayCutout();
+
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_never() {
+        addDisplayCutout();
+
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
+        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
+        addDisplayCutout();
+
+        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreen() {
+        addDisplayCutout();
+
+        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
+        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
+        addDisplayCutout();
+
+        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+    }
+
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_landscape() {
+        addDisplayCutout();
+        setRotation(ROTATION_90);
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mWindow.getContentFrameLw(),
+                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_seascape() {
+        addDisplayCutout();
+        setRotation(ROTATION_270);
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+        assertInsetBy(mWindow.getContentFrameLw(),
+                NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
+        addDisplayCutout();
+        setRotation(ROTATION_90);
+
+        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mWindow.getContentFrameLw(),
+                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
+        addDisplayCutout();
+
+        mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
+        mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
+        mWindow.mAttrs.width = DISPLAY_WIDTH;
+        mWindow.mAttrs.height = DISPLAY_HEIGHT;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
+        addDisplayCutout();
+        setRotation(ROTATION_90);
+
+        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mWindow.getContentFrameLw(),
+                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutHint_appWindow() {
+        // Initialize DisplayFrames
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+        final Rect outFrame = new Rect();
+        final Rect outContentInsets = new Rect();
+        final Rect outStableInsets = new Rect();
+        final Rect outOutsets = new Rect();
+        final DisplayCutout.ParcelableWrapper outDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+
+        mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames,
+                false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
+                outDisplayCutout);
+
+        assertThat(outFrame, is(mFrames.mUnrestricted));
+        assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+        assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+        assertThat(outOutsets, is(new Rect()));
+        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+    }
+
+    @Test
+    public void layoutHint_appWindowInTask() {
+        // Initialize DisplayFrames
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+        final Rect taskBounds = new Rect(100, 100, 200, 200);
+
+        final Rect outFrame = new Rect();
+        final Rect outContentInsets = new Rect();
+        final Rect outStableInsets = new Rect();
+        final Rect outOutsets = new Rect();
+        final DisplayCutout.ParcelableWrapper outDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+
+        mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
+                false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
+                outDisplayCutout);
+
+        assertThat(outFrame, is(taskBounds));
+        assertThat(outContentInsets, is(new Rect()));
+        assertThat(outStableInsets, is(new Rect()));
+        assertThat(outOutsets, is(new Rect()));
+        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+    }
+
+    @Test
+    public void layoutHint_appWindowInTask_outsideContentFrame() {
+        // Initialize DisplayFrames
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+        // Task is in the nav bar area (usually does not happen, but this is similar enough to the
+        // possible overlap with the IME)
+        final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
+                200, mFrames.mContent.bottom + 10);
+
+        final Rect outFrame = new Rect();
+        final Rect outContentInsets = new Rect();
+        final Rect outStableInsets = new Rect();
+        final Rect outOutsets = new Rect();
+        final DisplayCutout.ParcelableWrapper outDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+
+        mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
+                true /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
+                outDisplayCutout);
+
+        assertThat(outFrame, is(taskBounds));
+        assertThat(outContentInsets, is(new Rect()));
+        assertThat(outStableInsets, is(new Rect()));
+        assertThat(outOutsets, is(new Rect()));
+        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+    }
+
+    /**
+     * Asserts that {@code actual} is inset by the given amounts from the full display rect.
+     *
+     * Convenience wrapper for when only the top and bottom inset are non-zero.
+     */
+    private void assertInsetByTopBottom(Rect actual, int expectedInsetTop,
+            int expectedInsetBottom) {
+        assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
+    }
+
+    /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
+    private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
+            int expectedInsetRight, int expectedInsetBottom) {
+        assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
+                mFrames.mDisplayWidth - expectedInsetRight,
+                mFrames.mDisplayHeight - expectedInsetBottom), actual);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java
new file mode 100644
index 0000000..07d5fea
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.graphics.PixelFormat;
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DisplayPolicyTests extends WindowTestsBase {
+
+    private WindowState createOpaqueFullscreen(boolean hasLightNavBar) {
+        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "opaqueFullscreen");
+        final WindowManager.LayoutParams attrs = win.mAttrs;
+        attrs.width = MATCH_PARENT;
+        attrs.height = MATCH_PARENT;
+        attrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        attrs.format = PixelFormat.OPAQUE;
+        attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility =
+                hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+        return win;
+    }
+
+    private WindowState createDimmingDialogWindow(boolean canBeImTarget) {
+        final WindowState win = spy(createWindow(null, TYPE_APPLICATION, "dimmingDialog"));
+        final WindowManager.LayoutParams attrs = win.mAttrs;
+        attrs.width = WRAP_CONTENT;
+        attrs.height = WRAP_CONTENT;
+        attrs.flags = FLAG_DIM_BEHIND | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM);
+        attrs.format = PixelFormat.TRANSLUCENT;
+        when(win.isDimming()).thenReturn(true);
+        return win;
+    }
+
+    private WindowState createInputMethodWindow(boolean visible, boolean drawNavBar,
+            boolean hasLightNavBar) {
+        final WindowState win = createWindow(null, TYPE_INPUT_METHOD, "inputMethod");
+        final WindowManager.LayoutParams attrs = win.mAttrs;
+        attrs.width = MATCH_PARENT;
+        attrs.height = MATCH_PARENT;
+        attrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN
+                | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0);
+        attrs.format = PixelFormat.TRANSPARENT;
+        attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility =
+                hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+        win.mHasSurface = visible;
+        return win;
+    }
+
+    @Test
+    public void testChooseNavigationColorWindowLw() {
+        final WindowState opaque = createOpaqueFullscreen(false);
+
+        final WindowState dimmingImTarget = createDimmingDialogWindow(true);
+        final WindowState dimmingNonImTarget = createDimmingDialogWindow(false);
+
+        final WindowState visibleIme = createInputMethodWindow(true, true, false);
+        final WindowState invisibleIme = createInputMethodWindow(false, true, false);
+        final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
+
+        // If everything is null, return null
+        assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw(
+                null, null, null, NAV_BAR_BOTTOM));
+
+        assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, opaque, null, NAV_BAR_BOTTOM));
+        assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, dimmingImTarget, null, NAV_BAR_BOTTOM));
+        assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM));
+
+        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+                null, null, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+                null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+                null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+
+        assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
+        assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
+        assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, opaque, visibleIme, NAV_BAR_RIGHT));
+
+        // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
+        // window.
+        assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+        assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+        assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+                opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+    }
+
+    @Test
+    public void testUpdateLightNavigationBarLw() {
+        final WindowState opaqueDarkNavBar = createOpaqueFullscreen(false);
+        final WindowState opaqueLightNavBar = createOpaqueFullscreen(true);
+
+        final WindowState dimming = createDimmingDialogWindow(false);
+
+        final WindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false);
+        final WindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true);
+
+        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                DisplayPolicy.updateLightNavigationBarLw(
+                        SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null,
+                        null, null));
+
+        // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag.
+        assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+                0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
+        assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null,
+                opaqueDarkNavBar));
+        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                DisplayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar,
+                        opaqueLightNavBar, null, opaqueLightNavBar));
+        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                DisplayPolicy.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                        opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
+
+        // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
+        assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+                0, opaqueDarkNavBar, dimming, null, dimming));
+        assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+                0, opaqueLightNavBar, dimming, null, dimming));
+        assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming));
+        assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming));
+        assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar,
+                dimming));
+
+        // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
+        assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar,
+                imeDrawDarkNavBar));
+
+        // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins.
+        assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar,
+                imeDrawDarkNavBar, imeDrawDarkNavBar));
+
+        // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
+        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                DisplayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
+                        opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java
new file mode 100644
index 0000000..1d63c57
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.os.IBinder;
+import android.testing.TestableResources;
+import android.util.Pair;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.R;
+import com.android.server.wm.utils.WmDisplayCutout;
+
+import org.junit.Before;
+
+public class DisplayPolicyTestsBase extends WindowTestsBase {
+
+    static final int DISPLAY_WIDTH = 500;
+    static final int DISPLAY_HEIGHT = 1000;
+    static final int DISPLAY_DENSITY = 320;
+
+    static final int STATUS_BAR_HEIGHT = 10;
+    static final int NAV_BAR_HEIGHT = 15;
+    static final int DISPLAY_CUTOUT_HEIGHT = 8;
+
+    DisplayPolicy mDisplayPolicy;
+
+    @Before
+    public void setUpBase() {
+        super.setUpBase();
+        mDisplayPolicy = spy(mDisplayContent.getDisplayPolicy());
+
+        final TestContextWrapper context =
+                new TestContextWrapper(mDisplayPolicy.getSystemUiContext());
+        final TestableResources resources = context.getResourceMocker();
+        resources.addOverride(R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT);
+        resources.addOverride(R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT);
+        resources.addOverride(R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
+        resources.addOverride(R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT);
+        resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
+        when(mDisplayPolicy.getSystemUiContext()).thenReturn(context);
+        when(mDisplayPolicy.hasNavigationBar()).thenReturn(true);
+
+        final int shortSizeDp =
+                Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
+        final int longSizeDp =
+                Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
+        mDisplayContent.getDisplayRotation().configure(
+                DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp, longSizeDp);
+        mDisplayPolicy.configure(DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp);
+        mDisplayPolicy.onConfigurationChanged();
+
+        mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
+        addWindow(mStatusBarWindow);
+        mDisplayPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT;
+
+        mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
+        addWindow(mNavBarWindow);
+        mDisplayPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
+    }
+
+    void addWindow(WindowState win) {
+        mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, true /* hasStatusBarPermission */);
+        assertEquals(WindowManagerGlobal.ADD_OKAY,
+                mDisplayPolicy.prepareAddWindowLw(win, win.mAttrs));
+        win.mHasSurface = true;
+    }
+
+    static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
+            boolean withDisplayCutout) {
+        final DisplayInfo info = new DisplayInfo();
+        WmDisplayCutout cutout = null;
+
+        final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
+        info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+        info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+        info.rotation = rotation;
+        if (withDisplayCutout) {
+            cutout = WmDisplayCutout.computeSafeInsets(
+                    displayCutoutForRotation(rotation), info.logicalWidth,
+                    info.logicalHeight);
+            info.displayCutout = cutout.getDisplayCutout();
+        } else {
+            info.displayCutout = null;
+        }
+        return Pair.create(info, cutout);
+    }
+
+    private static DisplayCutout displayCutoutForRotation(int rotation) {
+        final RectF rectF =
+                new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
+
+        final Matrix m = new Matrix();
+        transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
+        m.mapRect(rectF);
+
+        int pos = -1;
+        switch (rotation) {
+            case ROTATION_0:
+                pos = BOUNDS_POSITION_TOP;
+                break;
+            case ROTATION_90:
+                pos = BOUNDS_POSITION_LEFT;
+                break;
+            case ROTATION_180:
+                pos = BOUNDS_POSITION_BOTTOM;
+                break;
+            case ROTATION_270:
+                pos = BOUNDS_POSITION_RIGHT;
+                break;
+        }
+
+        return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
+                (int) rectF.right, (int) rectF.bottom, pos);
+    }
+
+    static class TestContextWrapper extends ContextWrapper {
+        private final TestableResources mResourceMocker;
+
+        TestContextWrapper(Context targetContext) {
+            super(targetContext);
+            mResourceMocker = new TestableResources(targetContext.getResources());
+        }
+
+        @Override
+        public int checkPermission(String permission, int pid, int uid) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
+        @Override
+        public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
+        @Override
+        public Resources getResources() {
+            return mResourceMocker.getResources();
+        }
+
+        TestableResources getResourceMocker() {
+            return mResourceMocker;
+        }
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java
new file mode 100644
index 0000000..a04bf16
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DockedStackDividerControllerTests {
+
+    @Test
+    public void testIsDockSideAllowedDockTop() {
+        // Docked top is always allowed
+        assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT,
+                NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
+        assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT,
+                NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedDockBottom() {
+        // Cannot dock bottom
+        assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT,
+                NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedNavigationBarMovable() {
+        assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
+                NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
+        assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
+                NAV_BAR_LEFT, true /* navigationBarCanMove */));
+        assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
+                NAV_BAR_RIGHT, true /* navigationBarCanMove */));
+        assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
+                NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
+        assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
+                NAV_BAR_RIGHT, true /* navigationBarCanMove */));
+        assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
+                NAV_BAR_LEFT, true /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedNavigationBarNotMovable() {
+        // Navigation bar is not movable such as tablets
+        assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
+                NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+        assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP,
+                NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+        assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT,
+                NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+        assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
+                NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+        assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP,
+                NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+        assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT,
+                NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index fe5fc06..ee3bba7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -123,6 +123,8 @@
         final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         hiddenAppWindow.setHidden(true);
+        mDisplayContent.getConfiguration().windowConfiguration.setRotation(
+                mDisplayContent.getRotation());
         mController.initialize(mDisplayContent, ACTIVITY_TYPE_HOME, new SparseBooleanArray());
 
         // Ensure that we are animating the target activity as well
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 0165e7d..7b542cb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -26,12 +26,10 @@
 import android.content.Context;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.proto.ProtoOutputStream;
-import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowManager;
 import android.view.KeyEvent;
@@ -48,7 +46,6 @@
 class TestWindowManagerPolicy implements WindowManagerPolicy {
     private final Supplier<WindowManagerService> mWmSupplier;
 
-    int rotationToReport = 0;
     boolean keyguardShowingAndNotOccluded = false;
 
     private Runnable mRunnableWhenAddingSplashScreen;
@@ -81,11 +78,6 @@
     }
 
     @Override
-    public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
-            boolean hasStatusBarServicePermission) {
-    }
-
-    @Override
     public void adjustConfigurationLw(Configuration config, int keyboardPresence,
             int navigationPresence) {
     }
@@ -96,30 +88,6 @@
     }
 
     @Override
-    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
-            int displayId, DisplayCutout displayCutout) {
-        return 0;
-    }
-
-    @Override
-    public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
-            int displayId, DisplayCutout displayCutout) {
-        return 0;
-    }
-
-    @Override
-    public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
-            int displayId, DisplayCutout displayCutout) {
-        return 0;
-    }
-
-    @Override
-    public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
-            int displayId, DisplayCutout displayCutout) {
-        return 0;
-    }
-
-    @Override
     public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
         return attrs.type == TYPE_STATUS_BAR;
     }
@@ -166,28 +134,7 @@
     }
 
     @Override
-    public int prepareAddWindowLw(WindowState win,
-            WindowManager.LayoutParams attrs) {
-        return 0;
-    }
-
-    @Override
-    public void removeWindowLw(WindowState win) {
-    }
-
-    @Override
-    public int selectAnimationLw(WindowState win, int transit) {
-        return 0;
-    }
-
-    @Override
-    public void selectRotationAnimationLw(int[] anim) {
-    }
-
-    @Override
-    public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId,
-            boolean forceDefault) {
-        return false;
+    public void setKeyguardCandidateLw(WindowState win) {
     }
 
     @Override
@@ -222,32 +169,11 @@
     }
 
     @Override
-    public int getSystemDecorLayerLw() {
-        return 0;
+    public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) {
     }
 
     @Override
-    public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
-    }
-
-    @Override
-    public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
-            WindowState attached, WindowState imeTarget) {
-    }
-
-    @Override
-    public int finishPostLayoutPolicyLw() {
-        return 0;
-    }
-
-    @Override
-    public boolean allowAppAnimationsLw() {
-        return false;
-    }
-
-    @Override
-    public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
-        return 0;
+    public void setAllowLockscreenWhenOn(int displayId, boolean allow) {
     }
 
     @Override
@@ -349,11 +275,6 @@
     }
 
     @Override
-    public boolean isShowingDreamLw() {
-        return false;
-    }
-
-    @Override
     public void onKeyguardOccludedChangedLw(boolean occluded) {
     }
 
@@ -399,11 +320,6 @@
     }
 
     @Override
-    public int adjustSystemUiVisibilityLw(int visibility) {
-        return 0;
-    }
-
-    @Override
     public boolean hasNavigationBar() {
         return false;
     }
@@ -421,6 +337,16 @@
     }
 
     @Override
+    public boolean isUserSetupComplete() {
+        return false;
+    }
+
+    @Override
+    public int getUiMode() {
+        return 0;
+    }
+
+    @Override
     public void setCurrentUserLw(int newUserId) {
     }
 
@@ -446,43 +372,6 @@
     }
 
     @Override
-    public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
-            DisplayCutout cutout, Rect outInsets) {
-    }
-
-    @Override
-    public boolean isNavBarForcedShownLw(WindowState win) {
-        return false;
-    }
-
-    @NavigationBarPosition
-    @Override
-    public int getNavBarPosition() {
-        return NAV_BAR_BOTTOM;
-    }
-
-    @Override
-    public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
-            DisplayCutout cutout, Rect outInsets) {
-    }
-
-    @Override
-    public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
-            int displayHeight, int displayRotation) {
-        return false;
-    }
-
-    @Override
-    public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
-    }
-
-    @Override
-    public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation,
-            int newRotation) {
-        return false;
-    }
-
-    @Override
     public void setPipVisibilityLw(boolean visible) {
     }
 
@@ -508,10 +397,6 @@
     }
 
     @Override
-    public void onLockTaskStateChangedLw(int lockTaskState) {
-    }
-
-    @Override
     public boolean setAodShowing(boolean aodShowing) {
         return false;
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
index 25e73e3..4ea6b39 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -48,7 +48,7 @@
         synchronized (mWm.mGlobalLock) {
             // No wallpaper
             final DisplayContent dc = createNewDisplay();
-            Bitmap wallpaperBitmap = mWm.mRoot.mWallpaperController.screenshotWallpaperLocked();
+            Bitmap wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
             assertNull(wallpaperBitmap);
 
             // No wallpaper WSA Surface
@@ -56,25 +56,25 @@
                     true, dc, true /* ownerCanManageAppTokens */);
             WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
                     wallpaperWindowToken, "wallpaperWindow");
-            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
             assertNull(wallpaperBitmap);
 
             // Wallpaper with not visible WSA surface.
             wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
             wallpaperWindow.mWinAnimator.mLastAlpha = 1;
-            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
             assertNull(wallpaperBitmap);
 
             when(windowSurfaceController.getShown()).thenReturn(true);
 
             // Wallpaper with WSA alpha set to 0.
             wallpaperWindow.mWinAnimator.mLastAlpha = 0;
-            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
             assertNull(wallpaperBitmap);
 
             // Wallpaper window with WSA Surface
             wallpaperWindow.mWinAnimator.mLastAlpha = 1;
-            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
             assertNotNull(wallpaperBitmap);
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index b0c8d8b..227eb00 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -113,9 +113,6 @@
 
     @Before
     public void setUp() throws Exception {
-        // Just any non zero value.
-        mWm.mSystemDecorLayer = 10000;
-
         mWindowToken = WindowTestUtils.createTestAppWindowToken(
                 mWm.getDefaultDisplayContentLocked());
         mStubStack = new TaskStack(mWm, 0, null);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 80bb936..60c0459 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -27,21 +27,16 @@
 import static org.mockito.Mockito.anyFloat;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
-import android.view.Display;
 import android.view.IApplicationToken;
 import android.view.IWindow;
-import android.view.Surface;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
 
@@ -54,37 +49,6 @@
 public class WindowTestUtils {
     private static int sNextTaskId = 0;
 
-    /** An extension of {@link DisplayContent} to gain package scoped access. */
-    public static class TestDisplayContent extends DisplayContent {
-
-        private TestDisplayContent(Display display, WindowManagerService service,
-                WallpaperController wallpaperController, DisplayWindowController controller) {
-            super(display, service, wallpaperController, controller);
-        }
-
-        /** Create a mocked default {@link DisplayContent}. */
-        public static TestDisplayContent create(Context context) {
-            final TestDisplayContent displayContent = mock(TestDisplayContent.class);
-            displayContent.isDefaultDisplay = true;
-
-            final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
-            when(displayPolicy.navigationBarCanMove()).thenReturn(true);
-            when(displayPolicy.hasNavigationBar()).thenReturn(true);
-
-            final DisplayRotation displayRotation = new DisplayRotation(
-                    mock(WindowManagerService.class), displayContent, displayPolicy,
-                    context, new Object());
-            displayRotation.mPortraitRotation = Surface.ROTATION_0;
-            displayRotation.mLandscapeRotation = Surface.ROTATION_90;
-            displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
-            displayRotation.mSeascapeRotation = Surface.ROTATION_270;
-
-            when(displayContent.getDisplayRotation()).thenReturn(displayRotation);
-
-            return displayContent;
-        }
-    }
-
     /**
      * Creates a mock instance of {@link StackWindowController}.
      */
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 53858c7..1eb46fb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -90,7 +90,6 @@
     WindowState mChildAppWindowAbove;
     WindowState mChildAppWindowBelow;
     HashSet<WindowState> mCommonWindows;
-    WallpaperController mWallpaperController;
 
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -119,8 +118,6 @@
             mWm = mWmRule.getWindowManagerService();
             beforeCreateDisplay();
 
-            mWallpaperController = new WallpaperController(mWm);
-
             context.getDisplay().getDisplayInfo(mDisplayInfo);
             mDisplayContent = createNewDisplay();
             mWm.mDisplayEnabled = true;
@@ -363,8 +360,7 @@
         final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                 displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
         synchronized (mWm.mGlobalLock) {
-            return new DisplayContent(display, mWm, mWallpaperController,
-                    mock(DisplayWindowController.class));
+            return new DisplayContent(display, mWm, mock(DisplayWindowController.class));
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java b/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
deleted file mode 100644
index d4f2b06..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.policy;
-
-import android.graphics.Rect;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-import android.view.IApplicationToken;
-import android.view.WindowManager;
-
-import com.android.server.wm.WindowFrames;
-
-public class FakeWindowState implements WindowManagerPolicy.WindowState {
-
-    private WindowFrames mWindowFrames = new WindowFrames();
-
-    public WindowManager.LayoutParams attrs;
-    public int displayId;
-    public boolean isVoiceInteraction;
-    public boolean inMultiWindowMode;
-    public boolean visible = true;
-    public int surfaceLayer = 1;
-    public boolean isDimming = false;
-
-    public boolean policyVisible = true;
-
-    @Override
-    public int getOwningUid() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public String getOwningPackage() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public void computeFrameLw() {
-    }
-
-    @Override
-    public Rect getFrameLw() {
-        return mWindowFrames.mParentFrame;
-    }
-
-    @Override
-    public Rect getDisplayFrameLw() {
-        return mWindowFrames.mDisplayFrame;
-    }
-
-    @Override
-    public Rect getOverscanFrameLw() {
-        return mWindowFrames.mOverscanFrame;
-    }
-
-    @Override
-    public Rect getContentFrameLw() {
-        return mWindowFrames.mContentFrame;
-    }
-
-    @Override
-    public Rect getVisibleFrameLw() {
-        return mWindowFrames.mVisibleFrame;
-    }
-
-    public Rect getStableFrame() {
-        return mWindowFrames.mStableFrame;
-    }
-
-    public Rect getDecorFrame() {
-        return mWindowFrames.mDecorFrame;
-    }
-
-    @Override
-    public boolean getGivenInsetsPendingLw() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public Rect getGivenContentInsetsLw() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public Rect getGivenVisibleInsetsLw() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public WindowManager.LayoutParams getAttrs() {
-        return attrs;
-    }
-
-    @Override
-    public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public int getSystemUiVisibility() {
-        return attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility;
-    }
-
-    @Override
-    public int getSurfaceLayer() {
-        return surfaceLayer;
-    }
-
-    @Override
-    public int getBaseType() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public IApplicationToken getAppToken() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isVoiceInteraction() {
-        return isVoiceInteraction;
-    }
-
-    @Override
-    public boolean hasAppShownWindows() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isVisibleLw() {
-        return visible && policyVisible;
-    }
-
-    @Override
-    public boolean isDisplayedLw() {
-        return isVisibleLw();
-    }
-
-    @Override
-    public boolean isAnimatingLw() {
-        return false;
-    }
-
-    @Override
-    public boolean canAffectSystemUiFlags() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isGoneForLayoutLw() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isDrawnLw() {
-        return true;
-    }
-
-    @Override
-    public boolean hasDrawnLw() {
-        return true;
-    }
-
-    @Override
-    public boolean hideLw(boolean doAnimation) {
-        if (!policyVisible) {
-            return false;
-        }
-        policyVisible = false;
-        return true;
-    }
-
-    @Override
-    public boolean showLw(boolean doAnimation) {
-        if (policyVisible) {
-            return false;
-        }
-        policyVisible = true;
-        return true;
-    }
-
-    @Override
-    public boolean isAlive() {
-        return true;
-    }
-
-    @Override
-    public boolean isDefaultDisplay() {
-        return displayId == Display.DEFAULT_DISPLAY;
-    }
-
-    @Override
-    public boolean isDimming() {
-        return isDimming;
-    }
-
-    @Override
-    public int getWindowingMode() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isInMultiWindowMode() {
-        return inMultiWindowMode;
-    }
-
-    @Override
-    public int getRotationAnimationHint() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isInputMethodWindow() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public int getDisplayId() {
-        return displayId;
-    }
-
-    @Override
-    public boolean canAcquireSleepToken() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean canReceiveKeys() {
-        return false;
-    }
-
-    @Override
-    public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public WindowFrames getWindowFrames() {
-        return mWindowFrames;
-    }
-
-    @Override
-    public boolean isInputMethodTarget() {
-        return false;
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
deleted file mode 100644
index e8f767a..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.policy;
-
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.view.DisplayCutout;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Build/Install/Run:
- *  atest WmTests:PhoneWindowManagerLayoutTest
- */
-@SmallTest
-@Presubmit
-public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
-
-    private FakeWindowState mAppWindow;
-
-    @Before
-    public void setUp() throws Exception {
-        mAppWindow = new FakeWindowState();
-        mAppWindow.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
-                TYPE_APPLICATION,
-                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
-                PixelFormat.TRANSLUCENT);
-
-        addStatusBar();
-        addNavigationBar();
-    }
-
-    @Test
-    public void layoutWindowLw_appDrawsBars() {
-        mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_appWontDrawBars() {
-        mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
-    }
-
-    @Test
-    public void layoutWindowLw_appWontDrawBars_forceStatus() {
-        mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
-    }
-
-    @Test
-    public void addingWindow_doesNotTamperWithSysuiFlags() {
-        mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mPolicy.addWindow(mAppWindow);
-
-        assertEquals(0, mAppWindow.attrs.systemUiVisibility);
-        assertEquals(0, mAppWindow.attrs.subtreeSystemUiVisibility);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout() {
-        addDisplayCutout();
-
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withhDisplayCutout_never() {
-        addDisplayCutout();
-
-        mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
-        addDisplayCutout();
-
-        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreen() {
-        addDisplayCutout();
-
-        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
-        addDisplayCutout();
-
-        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
-        mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
-    }
-
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_landscape() {
-        addDisplayCutout();
-        setRotation(ROTATION_90);
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getContentFrameLw(),
-                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mAppWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_seascape() {
-        addDisplayCutout();
-        setRotation(ROTATION_270);
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetBy(mAppWindow.getFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getStableFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
-        assertInsetBy(mAppWindow.getContentFrameLw(),
-                NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
-        addDisplayCutout();
-        setRotation(ROTATION_90);
-
-        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getContentFrameLw(),
-                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
-        addDisplayCutout();
-
-        mAppWindow.attrs.flags = FLAG_LAYOUT_IN_SCREEN;
-        mAppWindow.attrs.type = TYPE_APPLICATION_OVERLAY;
-        mAppWindow.attrs.width = DISPLAY_WIDTH;
-        mAppWindow.attrs.height = DISPLAY_HEIGHT;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
-        addDisplayCutout();
-        setRotation(ROTATION_90);
-
-        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetBy(mAppWindow.getFrameLw(), 0, 0, 0, 0);
-        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getContentFrameLw(),
-                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutHint_screenDecorWindow() {
-        addDisplayCutout();
-        mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        final Rect frame = new Rect();
-        final Rect content = new Rect();
-        final Rect stable = new Rect();
-        final Rect outsets = new Rect();
-        final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper();
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames,
-                false /* floatingStack */, frame, content, stable, outsets, cutout);
-
-        assertThat(frame, equalTo(mFrames.mUnrestricted));
-        assertThat(content, equalTo(new Rect()));
-        assertThat(stable, equalTo(new Rect()));
-        assertThat(outsets, equalTo(new Rect()));
-        assertThat(cutout.get(), equalTo(DisplayCutout.NO_CUTOUT));
-    }
-
-    @Test
-    public void layoutHint_appWindow() {
-        // Initialize DisplayFrames
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outOutsets = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, null, mFrames, false /* floatingStack */,
-                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
-
-        assertThat(outFrame, is(mFrames.mUnrestricted));
-        assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
-        assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
-        assertThat(outOutsets, is(new Rect()));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-    }
-
-    @Test
-    public void layoutHint_appWindowInTask() {
-        // Initialize DisplayFrames
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        final Rect taskBounds = new Rect(100, 100, 200, 200);
-
-        final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outOutsets = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, false /* floatingStack */,
-                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
-
-        assertThat(outFrame, is(taskBounds));
-        assertThat(outContentInsets, is(new Rect()));
-        assertThat(outStableInsets, is(new Rect()));
-        assertThat(outOutsets, is(new Rect()));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-    }
-
-    @Test
-    public void layoutHint_appWindowInTask_outsideContentFrame() {
-        // Initialize DisplayFrames
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        // Task is in the nav bar area (usually does not happen, but this is similar enough to the
-        // possible overlap with the IME)
-        final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
-                200, mFrames.mContent.bottom + 10);
-
-        final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outOutsets = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, true /* floatingStack */,
-                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
-
-        assertThat(outFrame, is(taskBounds));
-        assertThat(outContentInsets, is(new Rect()));
-        assertThat(outStableInsets, is(new Rect()));
-        assertThat(outOutsets, is(new Rect()));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
deleted file mode 100644
index 6c44d65..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.policy;
-
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.graphics.PixelFormat;
-import android.platform.test.annotations.Presubmit;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Build/Install/Run:
- *  atest WmTests:PhoneWindowManagerTest
- */
-@SmallTest
-@Presubmit
-public class PhoneWindowManagerTest {
-
-    private static FakeWindowState createOpaqueFullscreen(boolean hasLightNavBar) {
-        final FakeWindowState state = new FakeWindowState();
-        state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
-                TYPE_BASE_APPLICATION,
-                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
-                PixelFormat.OPAQUE);
-        state.attrs.subtreeSystemUiVisibility =
-                hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
-        return state;
-    }
-
-    private static FakeWindowState createDimmingDialogWindow(boolean canBeImTarget) {
-        final FakeWindowState state = new FakeWindowState();
-        state.attrs = new WindowManager.LayoutParams(WRAP_CONTENT, WRAP_CONTENT,
-                TYPE_APPLICATION,
-                FLAG_DIM_BEHIND  | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM),
-                PixelFormat.TRANSLUCENT);
-        state.isDimming = true;
-        return state;
-    }
-
-    private static FakeWindowState createInputMethodWindow(boolean visible, boolean drawNavBar,
-            boolean hasLightNavBar) {
-        final FakeWindowState state = new FakeWindowState();
-        state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
-                TYPE_INPUT_METHOD,
-                FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN
-                        | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0),
-                PixelFormat.TRANSPARENT);
-        state.attrs.subtreeSystemUiVisibility =
-                hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
-        state.visible = visible;
-        state.policyVisible = visible;
-        return state;
-    }
-
-
-    @Test
-    public void testChooseNavigationColorWindowLw() {
-        final FakeWindowState opaque = createOpaqueFullscreen(false);
-
-        final FakeWindowState dimmingImTarget = createDimmingDialogWindow(true);
-        final FakeWindowState dimmingNonImTarget = createDimmingDialogWindow(false);
-
-        final FakeWindowState visibleIme = createInputMethodWindow(true, true, false);
-        final FakeWindowState invisibleIme = createInputMethodWindow(false, true, false);
-        final FakeWindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
-
-        // If everything is null, return null
-        assertNull(null, PhoneWindowManager.chooseNavigationColorWindowLw(
-                null, null, null, NAV_BAR_BOTTOM));
-
-        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, null, NAV_BAR_BOTTOM));
-        assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingImTarget, null, NAV_BAR_BOTTOM));
-        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM));
-
-        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
-                null, null, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
-                null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
-
-        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
-        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
-        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, visibleIme, NAV_BAR_RIGHT));
-
-        // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
-        // window.
-        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM));
-        assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
-        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
-    }
-
-    @Test
-    public void testUpdateLightNavigationBarLw() {
-        final FakeWindowState opaqueDarkNavBar = createOpaqueFullscreen(false);
-        final FakeWindowState opaqueLightNavBar = createOpaqueFullscreen(true);
-
-        final FakeWindowState dimming = createDimmingDialogWindow(false);
-
-        final FakeWindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false);
-        final FakeWindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true);
-
-        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
-                PhoneWindowManager.updateLightNavigationBarLw(
-                        SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null,
-                        null, null));
-
-        // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag.
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null,
-                opaqueDarkNavBar));
-        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
-                PhoneWindowManager.updateLightNavigationBarLw(0, opaqueLightNavBar,
-                        opaqueLightNavBar, null, opaqueLightNavBar));
-        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
-                PhoneWindowManager.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
-                        opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
-
-        // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                0, opaqueDarkNavBar, dimming, null, dimming));
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                0, opaqueLightNavBar, dimming, null, dimming));
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming));
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming));
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar,
-                dimming));
-
-        // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar,
-                imeDrawDarkNavBar));
-
-        // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins.
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar,
-                imeDrawDarkNavBar, imeDrawDarkNavBar));
-
-        // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
-        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
-                PhoneWindowManager.updateLightNavigationBarLw(0, opaqueDarkNavBar,
-                        opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
-    }
-
-    @Test
-    public void testIsDockSideAllowedDockTop() {
-        // Docked top is always allowed
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                true /* navigationBarCanMove */));
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-    }
-
-    @Test
-    public void testIsDockSideAllowedDockBottom() {
-        // Cannot dock bottom
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                true /* navigationBarCanMove */));
-    }
-
-    @Test
-    public void testIsDockSideAllowedNavigationBarMovable() {
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                true /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_LEFT,
-                true /* navigationBarCanMove */));
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_RIGHT,
-                true /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                true /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_RIGHT,
-                true /* navigationBarCanMove */));
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_LEFT,
-                true /* navigationBarCanMove */));
-    }
-
-    @Test
-    public void testIsDockSideAllowedNavigationBarNotMovable() {
-        // Navigation bar is not movable such as tablets
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
deleted file mode 100644
index 02a33e0..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.policy;
-
-import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
-import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.server.wm.utils.CoordinateTransforms
-        .transformPhysicalToLogicalCoordinates;
-
-import static org.junit.Assert.assertEquals;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Matrix;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.os.IBinder;
-import android.os.UserHandle;
-import android.testing.TestableResources;
-import android.util.Pair;
-import android.view.Display;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
-import android.view.Gravity;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IAccessibilityManager;
-
-import com.android.server.policy.keyguard.KeyguardServiceDelegate;
-import com.android.server.wm.DisplayFrames;
-import com.android.server.wm.WindowTestUtils.TestDisplayContent;
-import com.android.server.wm.utils.WmDisplayCutout;
-
-import org.junit.Before;
-
-class PhoneWindowManagerTestBase {
-    static final int DISPLAY_WIDTH = 500;
-    static final int DISPLAY_HEIGHT = 1000;
-
-    static final int STATUS_BAR_HEIGHT = 10;
-    static final int NAV_BAR_HEIGHT = 15;
-    static final int DISPLAY_CUTOUT_HEIGHT = 8;
-
-    TestablePhoneWindowManager mPolicy;
-    TestContextWrapper mContext;
-    DisplayFrames mFrames;
-
-    FakeWindowState mStatusBar;
-    FakeWindowState mNavigationBar;
-    private boolean mHasDisplayCutout;
-    private int mRotation = ROTATION_0;
-
-    @Before
-    public void setUpBase() {
-        mContext = new TestContextWrapper(getInstrumentation().getTargetContext());
-        mContext.getResourceMocker().addOverride(
-                com.android.internal.R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT);
-        mContext.getResourceMocker().addOverride(
-                com.android.internal.R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT);
-        mContext.getResourceMocker().addOverride(
-                com.android.internal.R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
-        mContext.getResourceMocker().addOverride(
-                com.android.internal.R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT);
-        mContext.getResourceMocker().addOverride(
-                com.android.internal.R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
-
-        mPolicy = TestablePhoneWindowManager.create(mContext);
-
-        updateDisplayFrames();
-    }
-
-    public void setRotation(int rotation) {
-        mRotation = rotation;
-        updateDisplayFrames();
-    }
-
-    private void updateDisplayFrames() {
-        Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
-                mHasDisplayCutout);
-        mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info.first, info.second);
-    }
-
-    public void addStatusBar() {
-        mStatusBar = new FakeWindowState();
-        mStatusBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, STATUS_BAR_HEIGHT,
-                TYPE_STATUS_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT);
-        mStatusBar.attrs.gravity = Gravity.TOP;
-
-        mPolicy.addWindow(mStatusBar);
-        mPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT;
-    }
-
-    public void addNavigationBar() {
-        mNavigationBar = new FakeWindowState();
-        mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, NAV_BAR_HEIGHT,
-                TYPE_NAVIGATION_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT);
-        mNavigationBar.attrs.gravity = Gravity.BOTTOM;
-
-        mPolicy.addWindow(mNavigationBar);
-        mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
-    }
-
-    public void addDisplayCutout() {
-        mHasDisplayCutout = true;
-        updateDisplayFrames();
-    }
-
-    /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
-    public void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
-            int expectedInsetRight, int expectedInsetBottom) {
-        assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
-                mFrames.mDisplayWidth - expectedInsetRight,
-                mFrames.mDisplayHeight - expectedInsetBottom), actual);
-    }
-
-    /**
-     * Asserts that {@code actual} is inset by the given amounts from the full display rect.
-     *
-     * Convenience wrapper for when only the top and bottom inset are non-zero.
-     */
-    public void assertInsetByTopBottom(Rect actual, int expectedInsetTop, int expectedInsetBottom) {
-        assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
-    }
-
-    public static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
-        return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first;
-    }
-    public static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
-            boolean withDisplayCutout) {
-        DisplayInfo info = new DisplayInfo();
-        WmDisplayCutout cutout = null;
-
-        final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
-        info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
-        info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
-        info.rotation = rotation;
-        if (withDisplayCutout) {
-            cutout = WmDisplayCutout.computeSafeInsets(
-                    displayCutoutForRotation(rotation), info.logicalWidth,
-                    info.logicalHeight);
-            info.displayCutout = cutout.getDisplayCutout();
-        } else {
-            info.displayCutout = null;
-        }
-        return Pair.create(info, cutout);
-    }
-
-    private static DisplayCutout displayCutoutForRotation(int rotation) {
-        RectF rectF = new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
-
-        Matrix m = new Matrix();
-        transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
-        m.mapRect(rectF);
-
-        int pos = -1;
-        switch (rotation) {
-            case ROTATION_0:
-                pos = BOUNDS_POSITION_TOP;
-                break;
-            case ROTATION_90:
-                pos = BOUNDS_POSITION_LEFT;
-                break;
-            case ROTATION_180:
-                pos = BOUNDS_POSITION_BOTTOM;
-                break;
-            case ROTATION_270:
-                pos = BOUNDS_POSITION_RIGHT;
-                break;
-        }
-
-
-        return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
-                (int) rectF.right, (int) rectF.bottom, pos);
-    }
-
-    static class TestContextWrapper extends ContextWrapper {
-        private final TestableResources mResourceMocker;
-
-        TestContextWrapper(Context targetContext) {
-            super(targetContext);
-            mResourceMocker = new TestableResources(targetContext.getResources());
-        }
-
-        @Override
-        public int checkPermission(String permission, int pid, int uid) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
-
-        @Override
-        public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
-
-        @Override
-        public Resources getResources() {
-            return mResourceMocker.getResources();
-        }
-
-        public TestableResources getResourceMocker() {
-            return mResourceMocker;
-        }
-    }
-
-    static class TestablePhoneWindowManager extends PhoneWindowManager {
-
-        TestablePhoneWindowManager() {
-        }
-
-        @Override
-        void initializeHdmiState() {
-            // Do nothing.
-        }
-
-        @Override
-        Context getSystemUiContext() {
-            return mContext;
-        }
-
-        void addWindow(WindowState state) {
-            if (state instanceof FakeWindowState) {
-                ((FakeWindowState) state).surfaceLayer =
-                        getWindowLayerFromTypeLw(state.getAttrs().type,
-                                true /* canAddInternalSystemWindow */);
-            }
-            adjustWindowParamsLw(state, state.getAttrs(), true /* hasStatusBarPermission */);
-            assertEquals(WindowManagerGlobal.ADD_OKAY, prepareAddWindowLw(state, state.getAttrs()));
-        }
-
-        public static TestablePhoneWindowManager create(Context context) {
-            TestablePhoneWindowManager[] policy = new TestablePhoneWindowManager[1];
-            getInstrumentation().runOnMainSync(() -> {
-                policy[0] = new TestablePhoneWindowManager();
-                policy[0].mContext = context;
-                policy[0].mKeyguardDelegate = mock(KeyguardServiceDelegate.class);
-                policy[0].mAccessibilityManager = new AccessibilityManager(context,
-                        mock(IAccessibilityManager.class), UserHandle.USER_CURRENT);
-                policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class);
-
-                final TestDisplayContent displayContent = TestDisplayContent.create(context);
-                policy[0].setDefaultDisplay(displayContent);
-                policy[0].onConfigurationChanged(displayContent);
-            });
-            return policy[0];
-        }
-    }
-}
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 6ed83de..170bd33 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -163,7 +163,8 @@
     private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds,
             float aspectRatio, Rect expectedActivityBounds) {
         // Verify with nav bar on the right.
-        when(mService.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
+        when(mService.mWindowManager.getNavBarPosition(mActivity.getDisplayId()))
+                .thenReturn(navBarPosition);
         mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
         mActivity.info.maxAspectRatio = aspectRatio;
         mActivity.ensureActivityConfiguration(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 72d7c90..630a8bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -47,12 +47,10 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
+import java.io.InputStreamReader;
 import java.io.Reader;
 import java.util.ArrayList;
 
@@ -79,15 +77,10 @@
         final TaskRecord expected = createTaskRecord(64);
         expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
 
-        final File serializedFile = serializeToFile(expected);
-
-        try {
-            final TaskRecord actual = restoreFromFile(serializedFile);
-            assertEquals(expected.taskId, actual.taskId);
-            assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
-        } finally {
-            serializedFile.delete();
-        }
+        final byte[] serializedBytes = serializeToBytes(expected);
+        final TaskRecord actual = restoreFromBytes(serializedBytes);
+        assertEquals(expected.taskId, actual.taskId);
+        assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
     }
 
     @Test
@@ -131,10 +124,8 @@
         assertTrue(task.returnsToHomeStack());
     }
 
-    private File serializeToFile(TaskRecord r) throws IOException, XmlPullParserException {
-        final File tmpFile = File.createTempFile(r.taskId + "_task_", "xml");
-
-        try (OutputStream os = new FileOutputStream(tmpFile)) {
+    private byte[] serializeToBytes(TaskRecord r) throws IOException, XmlPullParserException {
+        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
             final XmlSerializer serializer = Xml.newSerializer();
             serializer.setOutput(os, "UTF-8");
             serializer.startDocument(null, true);
@@ -142,13 +133,14 @@
             r.saveToXml(serializer);
             serializer.endTag(null, TASK_TAG);
             serializer.endDocument();
-        }
 
-        return tmpFile;
+            os.flush();
+            return os.toByteArray();
+        }
     }
 
-    private TaskRecord restoreFromFile(File file) throws IOException, XmlPullParserException {
-        try (Reader reader = new BufferedReader(new FileReader(file))) {
+    private TaskRecord restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
+        try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
             final XmlPullParser parser = Xml.newPullParser();
             parser.setInput(reader);
             assertEquals(XmlPullParser.START_TAG, parser.next());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index cf34fe7..e56edab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -42,8 +42,8 @@
     public static class TestDisplayContent extends DisplayContent {
 
         private TestDisplayContent(Display display, WindowManagerService service,
-                WallpaperController wallpaperController, DisplayWindowController controller) {
-            super(display, service, wallpaperController, controller);
+                DisplayWindowController controller) {
+            super(display, service, controller);
         }
 
         /**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index bcc0e6b..5ad7c30 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1023,9 +1023,9 @@
     public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
 
     /**
-     * Default Enhanced 4G LTE mode enabled. When this is {@code true}, Enhanced 4G LTE mode by
-     * default is on, otherwise if {@code false}, Enhanced 4G LTE mode by default is off.
-     * @hide
+     * Sets the default state for the "Enhanced 4G LTE" or "Advanced Calling" mode toggle set by the
+     * user. When this is {@code true}, this mode by default is on, otherwise if {@code false},
+     * this mode by default is off.
      */
     public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL =
             "enhanced_4g_lte_on_by_default_bool";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8324f00..79ed93e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7696,7 +7696,7 @@
         try {
             return getITelephony().isAvailable(getSubId(),
                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE, getOpPackageName());
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
         } catch (RemoteException | NullPointerException ex) {
             return false;
         }
@@ -8557,6 +8557,26 @@
         return UNKNOWN_CARRIER_ID;
     }
 
+     /**
+      * Returns carrier id based on MCCMNC only. This is for fallback when exact carrier id
+      * {@link #getSimCarrierId()} configurations are not found
+      *
+      * @return matching carrier id from passing mccmnc.
+      * @hide
+      */
+     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+     public int getCarrierIdFromMccMnc(String mccmnc) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getCarrierIdFromMccMnc(getSlotIndex(), mccmnc);
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return UNKNOWN_CARRIER_ID;
+    }
+
     /**
      * Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
      * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index e06c372..122626f 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -28,17 +28,22 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.SubscriptionManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ITelephony;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.Executor;
 
 /**
@@ -52,6 +57,7 @@
  * @see #createForSubscriptionId(Context, int)
  * @hide
  */
+@SystemApi
 public class ImsMmTelManager {
 
     private static final String TAG = "ImsMmTelManager";
@@ -70,16 +76,12 @@
     /**
      * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
      * registration if signal quality degrades.
-     * @hide
      */
-    @SystemApi
     public static final int WIFI_MODE_WIFI_ONLY = 0;
 
     /**
      * Prefer registering for IMS over LTE if LTE signal quality is high enough.
-     * @hide
      */
-    @SystemApi
     public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
 
     /**
@@ -91,13 +93,26 @@
 
     /**
      * Callback class for receiving Registration callback events.
-     * @see #addImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
-     * @see #removeImsRegistrationCallback(RegistrationCallback)
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
+     * @see #unregisterImsRegistrationCallback(RegistrationCallback)
      */
     public static class RegistrationCallback {
 
         private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
 
+            // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
+            // and WWAN are more accurate constants.
+            private static final Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP =
+                    new HashMap<Integer, Integer>() {{
+                        // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
+                        // case, since it is defined.
+                        put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
+                        put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                                AccessNetworkConstants.TransportType.WWAN);
+                        put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                                AccessNetworkConstants.TransportType.WLAN);
+                    }};
+
             private final RegistrationCallback mLocalCallback;
             private Executor mExecutor;
 
@@ -109,16 +124,16 @@
             public void onRegistered(int imsRadioTech) {
                 if (mLocalCallback == null) return;
 
-                Binder.withCleanCallingIdentity(() ->
-                        mExecutor.execute(() -> mLocalCallback.onRegistered(imsRadioTech)));
+                Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+                        mLocalCallback.onRegistered(getAccessType(imsRadioTech))));
             }
 
             @Override
             public void onRegistering(int imsRadioTech) {
                 if (mLocalCallback == null) return;
 
-                Binder.withCleanCallingIdentity(() ->
-                        mExecutor.execute(() -> mLocalCallback.onRegistering(imsRadioTech)));
+                Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+                        mLocalCallback.onRegistering(getAccessType(imsRadioTech))));
             }
 
             @Override
@@ -134,8 +149,8 @@
                 if (mLocalCallback == null) return;
 
                 Binder.withCleanCallingIdentity(() ->
-                        mExecutor.execute(() ->
-                                mLocalCallback.onTechnologyChangeFailed(imsRadioTech, info)));
+                        mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
+                                getAccessType(imsRadioTech), info)));
             }
 
             @Override
@@ -150,6 +165,15 @@
             private void setExecutor(Executor executor) {
                 mExecutor = executor;
             }
+
+            private static int getAccessType(int regType) {
+                if (!IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
+                    Log.w("ImsMmTelManager", "RegistrationBinder - invalid regType returned: "
+                            + regType);
+                    return -1;
+                }
+                return IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
+            }
         }
 
         private final RegistrationBinder mBinder = new RegistrationBinder(this);
@@ -157,19 +181,19 @@
         /**
          * Notifies the framework when the IMS Provider is registered to the IMS network.
          *
-         * @param imsRadioTech the radio access technology. Valid values are defined in
-         * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+         * @param imsTransportType the radio access technology. Valid values are defined in
+         * {@link android.telephony.AccessNetworkConstants.TransportType}.
          */
-        public void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+        public void onRegistered(int imsTransportType) {
         }
 
         /**
          * Notifies the framework when the IMS Provider is trying to register the IMS network.
          *
-         * @param imsRadioTech the radio access technology. Valid values are defined in
-         * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+         * @param imsTransportType the radio access technology. Valid values are defined in
+         * {@link android.telephony.AccessNetworkConstants.TransportType}.
          */
-        public void onRegistering(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+        public void onRegistering(int imsTransportType) {
         }
 
         /**
@@ -182,14 +206,14 @@
 
         /**
          * A failure has occurred when trying to handover registration to another technology type,
-         * defined in {@link ImsRegistrationImplBase.ImsRegistrationTech}
+         * defined in {@link android.telephony.AccessNetworkConstants.TransportType}
          *
-         * @param imsRadioTech The {@link ImsRegistrationImplBase.ImsRegistrationTech} type that has
-         *         failed
+         * @param imsTransportType The
+         *         {@link android.telephony.AccessNetworkConstants.TransportType}
+         *         transport type that has failed to handover registration to.
          * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
          */
-        public void onTechnologyChangeFailed(
-                @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) {
+        public void onTechnologyChangeFailed(int imsTransportType, ImsReasonInfo info) {
         }
 
         /**
@@ -219,8 +243,8 @@
     /**
      * Receives IMS capability status updates from the ImsService.
      *
-     * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
-     * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+     * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
+     * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
      */
     public static class CapabilityCallback {
 
@@ -285,13 +309,12 @@
         }
     }
 
-    private Context mContext;
     private int mSubId;
 
     /**
      * Create an instance of ImsManager for the subscription id specified.
      *
-     * @param context
+     * @param context The context to create this ImsMmTelManager instance within.
      * @param subId The ID of the subscription that this ImsMmTelManager will use.
      * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
      * @throws IllegalArgumentException if the subscription is invalid or
@@ -303,11 +326,15 @@
             throw new IllegalArgumentException("Invalid subscription ID");
         }
 
-        return new ImsMmTelManager(context, subId);
+        return new ImsMmTelManager(subId);
     }
 
-    private ImsMmTelManager(Context context, int subId) {
-        mContext = context;
+    /**
+     * Only visible for testing, use {@link #createForSubscriptionId(Context, int)} instead.
+     * @hide
+     */
+    @VisibleForTesting
+    public ImsMmTelManager(int subId) {
         mSubId = subId;
     }
 
@@ -315,14 +342,18 @@
      * Registers a {@link RegistrationCallback} with the system, which will provide registration
      * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use
      * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
-     * events and call {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up
+     * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up
      * after a subscription is removed.
+     *
+     * When the callback is registered, it will initiate the callback c to be called with the
+     * current registration state.
+     *
      * @param executor The executor the callback events should be run on.
      * @param c The {@link RegistrationCallback} to be added.
-     * @see #removeImsRegistrationCallback(RegistrationCallback)
+     * @see #unregisterImsRegistrationCallback(RegistrationCallback)
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
-    public void addImsRegistrationCallback(@CallbackExecutor Executor executor,
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerImsRegistrationCallback(@CallbackExecutor Executor executor,
             @NonNull RegistrationCallback c) {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -332,8 +363,7 @@
         }
         c.setExecutor(executor);
         try {
-            getITelephony().addImsRegistrationCallback(mSubId, c.getBinder(),
-                    mContext.getOpPackageName());
+            getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -344,16 +374,15 @@
      * up to avoid memory leaks or when the subscription is removed.
      * @param c The {@link RegistrationCallback} to be removed.
      * @see SubscriptionManager.OnSubscriptionsChangedListener
-     * @see #addImsRegistrationCallback(Executor, RegistrationCallback)
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
-    public void removeImsRegistrationCallback(@NonNull RegistrationCallback c) {
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
         try {
-            getITelephony().removeImsRegistrationCallback(mSubId, c.getBinder(),
-                    mContext.getOpPackageName());
+            getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -364,14 +393,18 @@
      * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}.
      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
      * subscription changed events and call
-     * {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up after a subscription
-     * is removed.
+     * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up after a
+     * subscription is removed.
+     *
+     * When the callback is registered, it will initiate the callback c to be called with the
+     * current capabilities.
+     *
      * @param executor The executor the callback events should be run on.
      * @param c The MmTel {@link CapabilityCallback} to be registered.
-     * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+     * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
-    public void addMmTelCapabilityCallback(@CallbackExecutor Executor executor,
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerMmTelCapabilityCallback(@CallbackExecutor Executor executor,
             @NonNull CapabilityCallback c) {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -381,8 +414,7 @@
         }
         c.setExecutor(executor);
         try {
-            getITelephony().addMmTelCapabilityCallback(mSubId, c.getBinder(),
-                    mContext.getOpPackageName());
+            getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -392,29 +424,42 @@
      * Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning
      * up to avoid memory leaks.
      * @param c The MmTel {@link CapabilityCallback} to be removed.
-     * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback)
+     * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
-    public void removeMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
         try {
-            getITelephony().removeMmTelCapabilityCallback(mSubId, c.getBinder(),
-                    mContext.getOpPackageName());
+            getITelephony().unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     /**
-     * Query the user's setting for whether or not to use MmTel capabilities over IMS,
-     * such as voice and video, depending on carrier configuration for the current subscription.
+     * Query the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+     * enable MmTel IMS features, depending on the carrier configuration for the current
+     * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+     * be enabled as long as the carrier has provisioned these services for the specified
+     * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+     * carrier requirements.
+     *
+     * Modifying this value may also trigger an IMS registration or deregistration, depending on
+     * whether or not the new value is enabled or disabled.
+     *
+     * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+     * method will do nothing and will instead always use the default value.
+     *
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
      * @see #setAdvancedCallingSetting(boolean)
-     * @return true if the user’s setting for advanced calling is enabled and false otherwise.
-     * @hide
+     * @return true if the user's setting for advanced calling is enabled, false otherwise.
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isAdvancedCallingSettingEnabled() {
         try {
@@ -426,13 +471,25 @@
 
     /**
      * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
-     * enable MmTel IMS features, such as voice and video calling, depending on the carrier
-     * configuration for the current subscription. Modifying this value may also trigger an IMS
-     * registration or deregistration, depending on the new value.
-     * @see #isAdvancedCallingEnabled()
-     * @hide
+     * enable MmTel IMS features, depending on the carrier configuration for the current
+     * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+     * be enabled as long as the carrier has provisioned these services for the specified
+     * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+     * carrier requirements.
+     *
+     * Modifying this value may also trigger an IMS registration or deregistration, depending on
+     * whether or not the new value is enabled or disabled.
+     *
+     * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+     * method will do nothing and will instead always use the default value.
+     *
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+     * @see #isAdvancedCallingSettingEnabled()
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setAdvancedCallingSetting(boolean isEnabled) {
         try {
@@ -464,12 +521,11 @@
      * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
      *         otherwise.
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
         try {
-            return getITelephony().isCapable(mSubId, capability, imsRegTech,
-                    mContext.getOpPackageName());
+            return getITelephony().isCapable(mSubId, capability, imsRegTech);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -492,12 +548,11 @@
      * @return {@code true} if the MmTel IMS capability is available for this subscription, false
      *         otherwise.
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
         try {
-            return getITelephony().isAvailable(mSubId, capability, imsRegTech,
-                    mContext.getOpPackageName());
+            return getITelephony().isAvailable(mSubId, capability, imsRegTech);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -508,11 +563,10 @@
      * @return true if the user’s “Video Calling” setting is currently enabled.
      * @see #setVtSetting(boolean)
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isVtSettingEnabled() {
         try {
-            return getITelephony().isVtSettingEnabled(mSubId, mContext.getOpPackageName());
+            return getITelephony().isVtSettingEnabled(mSubId);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -521,9 +575,7 @@
     /**
      * Change the user's setting for Video Telephony and enable the Video Telephony capability.
      * @see #isVtSettingEnabled()
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVtSetting(boolean isEnabled) {
         try {
@@ -537,9 +589,7 @@
     /**
      * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
      * @see #setVoWiFiSetting(boolean)
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isVoWiFiSettingEnabled() {
         try {
@@ -553,9 +603,7 @@
      * Sets the user's setting for whether or not Voice over WiFi is enabled.
      * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
      * @see #isVoWiFiSettingEnabled()
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiSetting(boolean isEnabled) {
         try {
@@ -570,9 +618,7 @@
      * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
      * if disabled.
      * @see #setVoWiFiRoamingSetting(boolean)
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isVoWiFiRoamingSettingEnabled() {
         try {
@@ -587,9 +633,7 @@
      * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
      *     false otherwise.
      * @see #isVoWiFiRoamingSettingEnabled()
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingSetting(boolean isEnabled) {
         try {
@@ -611,9 +655,7 @@
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @see #setVoWiFiSetting(boolean)
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
         try {
@@ -631,9 +673,7 @@
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @see #setVoWiFiSetting(boolean)
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @WiFiCallingMode int getVoWiFiModeSetting() {
         try {
@@ -651,9 +691,7 @@
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @see #getVoWiFiModeSetting()
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
         try {
@@ -674,9 +712,7 @@
      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @see #setVoWiFiRoamingSetting(boolean)
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
         try {
@@ -696,9 +732,7 @@
      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @see #getVoWiFiRoamingModeSetting()
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
         try {
@@ -712,9 +746,7 @@
     /**
      * Change the user's setting for RTT capability of this device.
      * @param isEnabled if true RTT will be enabled during calls.
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setRttCapabilitySetting(boolean isEnabled) {
         try {
@@ -729,9 +761,7 @@
      * @return true if TTY over VoLTE is supported
      * @see android.telecom.TelecomManager#getCurrentTtyMode
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     boolean isTtyOverVolteEnabled() {
         try {
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 3f22f98..b55866b 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -425,6 +425,12 @@
      */
     public final void addCapabilityCallback(IImsCapabilityCallback c) {
         mCapabilityCallbacks.register(c);
+        try {
+            // Notify the Capability callback that was just registered of the current capabilities.
+            c.onCapabilitiesStatusChanged(queryCapabilityStatus().mCapabilities);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "addCapabilityCallback: error accessing callback: " + e.getMessage());
+        }
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f0e8586..fc42de5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1372,6 +1372,15 @@
     String getSubscriptionPreciseCarrierName(int subId);
 
     /**
+     * Returns carrier id based on MCCMNC only. This will return a MNO carrier id used for fallback
+     * check when exact carrier id {@link #getSimCarrierId()} configurations are not found
+     *
+     * @return carrier id from passing mccmnc.
+     * @hide
+     */
+    int getCarrierIdFromMccMnc(int slotIndex, String mccmnc);
+
+    /**
      * Action set from carrier signalling broadcast receivers to enable/disable metered apns
      * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
      * @param subId the subscription ID that this action applies to.
@@ -1587,35 +1596,31 @@
     /**
      * Adds an IMS registration status callback for the subscription id specified.
      */
-    void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
-            String callingPackage);
+    void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c);
      /**
       * Removes an existing IMS registration status callback for the subscription specified.
       */
-    void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
-            String callingPackage);
+    void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c);
 
     /**
      * Adds an IMS MmTel capabilities callback for the subscription specified.
      */
-    void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
-            String callingPackage);
+    void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c);
 
     /**
      * Removes an existing IMS MmTel capabilities callback for the subscription specified.
      */
-    void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
-            String callingPackage);
+    void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c);
 
     /**
      * return true if the IMS MmTel capability for the given registration tech is capable.
      */
-    boolean isCapable(int subId, int capability, int regTech, String callingPackage);
+    boolean isCapable(int subId, int capability, int regTech);
 
     /**
      * return true if the IMS MmTel capability for the given registration tech is available.
      */
-    boolean isAvailable(int subId, int capability, int regTech, String callingPackage);
+    boolean isAvailable(int subId, int capability, int regTech);
 
     /**
      * Returns true if the user's setting for 4G LTE is enabled, for the subscription specified.
@@ -1630,7 +1635,7 @@
     /**
      * return true if the user's setting for VT is enabled for the subscription.
      */
-    boolean isVtSettingEnabled(int subId, String callingPackage);
+    boolean isVtSettingEnabled(int subId);
 
     /**
      * Modify the user's setting for whether or not VT is available for the subscrption specified.
@@ -1704,7 +1709,7 @@
      * Identify if the number is emergency number, based on all the active subscriptions.
      */
     boolean isCurrentEmergencyNumber(String number);
-    
+
     /**
      * Return a list of certs in hex string from loaded carrier privileges access rules.
      */
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index f91d74a..7842a1c 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -135,6 +135,7 @@
     method public boolean stopService(android.content.Intent);
     method public void unbindService(android.content.ServiceConnection);
     method public void unregisterReceiver(android.content.BroadcastReceiver);
+    method public void updateServiceGroup(android.content.ServiceConnection, int, int);
   }
 
   public deprecated class MockCursor implements android.database.Cursor {
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 66be6d9..ae6cd29 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -591,6 +591,11 @@
     }
 
     @Override
+    public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void unbindService(ServiceConnection conn) {
         throw new UnsupportedOperationException();
     }