Merge "Shortcut integration with AppSearch (Part 2)" into sc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 040a116..8bb03e9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2293,7 +2293,8 @@
     /** Returns the maximum amount of time this job could run for. */
     public long getMaxJobExecutionTimeMs(JobStatus job) {
         synchronized (mLock) {
-            return mQuotaController.getMaxJobExecutionTimeMsLocked(job);
+            return Math.min(mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 2faa836..b70e68b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -995,12 +995,18 @@
         if (standbyBucket == NEVER_INDEX) {
             return 0;
         }
+
         List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
+        final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
         if (sessions == null || sessions.size() == 0) {
+            // Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
+            // essentially run until they reach the maximum limit.
+            if (stats.windowSizeMs == mAllowedTimePerPeriodMs) {
+                return mMaxExecutionTimeMs;
+            }
             return mAllowedTimePerPeriodMs;
         }
 
-        final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
         final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
         final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
         final long allowedTimeRemainingMs = mAllowedTimePerPeriodMs - stats.executionTimeInWindowMs;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 316be0b..6ee57d6 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1021,17 +1021,19 @@
 package android.hardware.devicestate {
 
   public final class DeviceStateManager {
-    method public void addDeviceStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
     method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
     method @NonNull public int[] getSupportedStates();
-    method public void removeDeviceStateListener(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
+    method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
     method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
+    method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
     field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
     field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0
   }
 
-  public static interface DeviceStateManager.DeviceStateListener {
-    method public void onDeviceStateChanged(int);
+  public static interface DeviceStateManager.DeviceStateCallback {
+    method public default void onBaseStateChanged(int);
+    method public void onStateChanged(int);
+    method public default void onSupportedStatesChanged(@NonNull int[]);
   }
 
   public final class DeviceStateRequest {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 73cc13c8..a1dce77 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -320,6 +320,10 @@
     private static final String KEY_OVERRIDE_TASK_TRANSITION =
             "android:activity.overrideTaskTransition";
 
+    /** See {@link #setRemoveWithTaskOrganizer(boolean)}. */
+    private static final String KEY_REMOVE_WITH_TASK_ORGANIZER =
+            "android.activity.removeWithTaskOrganizer";
+
     /**
      * @see #setLaunchCookie
      * @hide
@@ -405,6 +409,7 @@
     private IRemoteTransition mRemoteTransition;
     private boolean mOverrideTaskTransition;
     private int mSplashScreenThemeResId;
+    private boolean mRemoveWithTaskOrganizer;
 
     /**
      * Create an ActivityOptions specifying a custom animation to run when
@@ -1155,6 +1160,7 @@
                 KEY_REMOTE_TRANSITION));
         mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
         mSplashScreenThemeResId = opts.getInt(KEY_SPLASH_SCREEN_THEME);
+        mRemoveWithTaskOrganizer = opts.getBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER);
     }
 
     /**
@@ -1624,6 +1630,22 @@
     }
 
     /**
+     * Sets whether to remove the task when TaskOrganizer, which is managing it, is destroyed.
+     * @hide
+     */
+    public void setRemoveWithTaskOrganizer(boolean remove) {
+        mRemoveWithTaskOrganizer = remove;
+    }
+
+    /**
+     * @return whether to remove the task when TaskOrganizer, which is managing it, is destroyed.
+     * @hide
+     */
+    public boolean getRemoveWithTaskOranizer() {
+        return mRemoveWithTaskOrganizer;
+    }
+
+    /**
      * Update the current values in this ActivityOptions from those supplied
      * in <var>otherOptions</var>.  Any values
      * defined in <var>otherOptions</var> replace those in the base options.
@@ -1857,6 +1879,9 @@
         if (mSplashScreenThemeResId != 0) {
             b.putInt(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResId);
         }
+        if (mRemoveWithTaskOrganizer) {
+            b.putBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER, mRemoveWithTaskOrganizer);
+        }
         return b;
     }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d53d20a..d352b27 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6755,6 +6755,12 @@
      */
     private static final int LOCAL_FLAG_FROM_PROTECTED_COMPONENT = 1 << 2;
 
+    /**
+     * Local flag indicating this instance had unfiltered extras copied into it. This could be
+     * from either {@link #putExtras(Intent)} when an unparceled Intent is provided or {@link
+     * #putExtras(Bundle)} when the provided Bundle has not been unparceled.
+     */
+    private static final int LOCAL_FLAG_UNFILTERED_EXTRAS = 1 << 3;
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // toUri() and parseUri() options.
@@ -10009,6 +10015,15 @@
                 mExtras.putAll(src.mExtras);
             }
         }
+        // If the provided Intent was unparceled and this is not an Intent delivered to a protected
+        // component then mark the extras as unfiltered. An Intent delivered to a protected
+        // component had to come from a trusted component, and if unfiltered data was copied to the
+        // delivered Intent then it would have been reported when that Intent left the sending
+        // process.
+        if ((src.mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0
+                && (src.mLocalFlags & LOCAL_FLAG_FROM_PROTECTED_COMPONENT) == 0) {
+            mLocalFlags |= LOCAL_FLAG_UNFILTERED_EXTRAS;
+        }
         return this;
     }
 
@@ -10023,6 +10038,10 @@
      * @see #removeExtra
      */
     public @NonNull Intent putExtras(@NonNull Bundle extras) {
+        // If the provided Bundle has not yet been unparceled then treat this as unfiltered extras.
+        if (extras.isParcelled()) {
+            mLocalFlags |= LOCAL_FLAG_UNFILTERED_EXTRAS;
+        }
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -11328,10 +11347,13 @@
         }
 
         // Detect cases where we're about to launch a potentially unsafe intent
-        if ((mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0
-                && (mLocalFlags & LOCAL_FLAG_FROM_PROTECTED_COMPONENT) == 0
-                && StrictMode.vmUnsafeIntentLaunchEnabled()) {
-            StrictMode.onUnsafeIntentLaunch(this);
+        if (StrictMode.vmUnsafeIntentLaunchEnabled()) {
+            if ((mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0
+                    && (mLocalFlags & LOCAL_FLAG_FROM_PROTECTED_COMPONENT) == 0) {
+                StrictMode.onUnsafeIntentLaunch(this);
+            } else if ((mLocalFlags & LOCAL_FLAG_UNFILTERED_EXTRAS) != 0) {
+                StrictMode.onUnsafeIntentLaunch(this);
+            }
         }
     }
 
diff --git a/core/java/android/hardware/devicestate/DeviceStateInfo.aidl b/core/java/android/hardware/devicestate/DeviceStateInfo.aidl
new file mode 100644
index 0000000..e856792
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.devicestate;
+
+parcelable DeviceStateInfo;
diff --git a/core/java/android/hardware/devicestate/DeviceStateInfo.java b/core/java/android/hardware/devicestate/DeviceStateInfo.java
new file mode 100644
index 0000000..bc6af37
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateInfo.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.devicestate;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+
+/**
+ * Information about the state of the device.
+ *
+ * @hide
+ */
+public final class DeviceStateInfo implements Parcelable {
+    /** Bit that indicates the {@link #supportedStates} field has changed. */
+    public static final int CHANGED_SUPPORTED_STATES = 1 << 0;
+
+    /** Bit that indicates the {@link #baseState} field has changed. */
+    public static final int CHANGED_BASE_STATE = 1 << 1;
+
+    /** Bit that indicates the {@link #currentState} field has changed. */
+    public static final int CHANGED_CURRENT_STATE = 1 << 2;
+
+    @IntDef(prefix = {"CHANGED_"}, flag = true, value = {
+            CHANGED_SUPPORTED_STATES,
+            CHANGED_BASE_STATE,
+            CHANGED_CURRENT_STATE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ChangeFlags {}
+
+    /**
+     * The list of states supported by the device.
+     */
+    @NonNull
+    public final int[] supportedStates;
+
+    /**
+     * The base (non-override) state of the device. The base state is the state of the device
+     * ignoring any override requests made through a call to {@link DeviceStateManager#requestState(
+     * DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+     */
+    public final int baseState;
+
+    /**
+     * The state of the device.
+     */
+    public final int currentState;
+
+    /**
+     * Creates a new instance of {@link DeviceStateInfo}.
+     * <p>
+     * NOTE: Unlike {@link #DeviceStateInfo(DeviceStateInfo)}, this constructor does not copy the
+     * supplied parameters.
+     */
+    public DeviceStateInfo(@NonNull int[] supportedStates, int baseState, int state) {
+        this.supportedStates = supportedStates;
+        this.baseState = baseState;
+        this.currentState = state;
+    }
+
+    /**
+     * Creates a new instance of {@link DeviceStateInfo} copying the fields of {@code info} into
+     * the fields of the returned instance.
+     */
+    public DeviceStateInfo(@NonNull DeviceStateInfo info) {
+        this(Arrays.copyOf(info.supportedStates, info.supportedStates.length),
+                info.baseState, info.currentState);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) return true;
+        if (other == null || getClass() != other.getClass()) return false;
+        DeviceStateInfo that = (DeviceStateInfo) other;
+        return baseState == that.baseState
+                &&  currentState == that.currentState
+                && Arrays.equals(supportedStates, that.supportedStates);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(baseState, currentState);
+        result = 31 * result + Arrays.hashCode(supportedStates);
+        return result;
+    }
+
+    /** Returns a bitmask of the differences between this instance and {@code other}. */
+    @ChangeFlags
+    public int diff(@NonNull DeviceStateInfo other) {
+        int diff = 0;
+        if (!Arrays.equals(supportedStates, other.supportedStates)) {
+            diff |= CHANGED_SUPPORTED_STATES;
+        }
+        if (baseState != other.baseState) {
+            diff |= CHANGED_BASE_STATE;
+        }
+        if (currentState != other.currentState) {
+            diff |= CHANGED_CURRENT_STATE;
+        }
+        return diff;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(supportedStates.length);
+        for (int i = 0; i < supportedStates.length; i++) {
+            dest.writeInt(supportedStates[i]);
+        }
+
+        dest.writeInt(baseState);
+        dest.writeInt(currentState);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<DeviceStateInfo> CREATOR = new Creator<DeviceStateInfo>() {
+        @Override
+        public DeviceStateInfo createFromParcel(Parcel source) {
+            final int numberOfSupportedStates = source.readInt();
+            final int[] supportedStates = new int[numberOfSupportedStates];
+            for (int i = 0; i < numberOfSupportedStates; i++) {
+                supportedStates[i] = source.readInt();
+            }
+            final int baseState = source.readInt();
+            final int currentState = source.readInt();
+
+            return new DeviceStateInfo(supportedStates, baseState, currentState);
+        }
+
+        @Override
+        public DeviceStateInfo[] newArray(int size) {
+            return new DeviceStateInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 2d4b2cc..250145e 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -111,38 +111,63 @@
     }
 
     /**
-     * Registers a listener to receive notifications about changes in device state.
+     * Registers a callback to receive notifications about changes in device state.
      *
      * @param executor the executor to process notifications.
-     * @param listener the listener to register.
+     * @param callback the callback to register.
      *
-     * @see DeviceStateListener
+     * @see DeviceStateCallback
      */
-    public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor,
-            @NonNull DeviceStateListener listener) {
-        mGlobal.registerDeviceStateListener(listener, executor);
+    public void registerCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull DeviceStateCallback callback) {
+        mGlobal.registerDeviceStateCallback(callback, executor);
     }
 
     /**
-     * Unregisters a listener previously registered with
-     * {@link #addDeviceStateListener(Executor, DeviceStateListener)}.
+     * Unregisters a callback previously registered with
+     * {@link #registerCallback(Executor, DeviceStateCallback)}.
      */
-    public void removeDeviceStateListener(@NonNull DeviceStateListener listener) {
-        mGlobal.unregisterDeviceStateListener(listener);
+    public void unregisterCallback(@NonNull DeviceStateCallback callback) {
+        mGlobal.unregisterDeviceStateCallback(callback);
     }
 
-    /**
-     * Listens for changes in device states.
-     */
-    public interface DeviceStateListener {
+    /** Callback to receive notifications about changes in device state. */
+    public interface DeviceStateCallback {
+        /**
+         * Called in response to a change in the states supported by the device.
+         * <p>
+         * Guaranteed to be called once on registration of the callback with the initial value and
+         * then on every subsequent change in the supported states.
+         *
+         * @param supportedStates the new supported states.
+         *
+         * @see DeviceStateManager#getSupportedStates()
+         */
+        default void onSupportedStatesChanged(@NonNull int[] supportedStates) {}
+
+        /**
+         * Called in response to a change in the base device state.
+         * <p>
+         * The base state is the state of the device without considering any requests made through
+         * calls to {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}
+         * from any client process. The base state is guaranteed to match the state provided with a
+         * call to {@link #onStateChanged(int)} when there are no active requests from any process.
+         * <p>
+         * Guaranteed to be called once on registration of the callback with the initial value and
+         * then on every subsequent change in the non-override state.
+         *
+         * @param state the new base device state.
+         */
+        default void onBaseStateChanged(int state) {}
+
         /**
          * Called in response to device state changes.
          * <p>
-         * Guaranteed to be called once on registration of the listener with the
-         * initial value and then on every subsequent change in device state.
+         * Guaranteed to be called once on registration of the callback with the initial value and
+         * then on every subsequent change in device state.
          *
-         * @param deviceState the new device state.
+         * @param state the new device state.
          */
-        void onDeviceStateChanged(int deviceState);
+        void onStateChanged(int state);
     }
 }
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index b9ae88e..1b37fb9 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.hardware.devicestate.DeviceStateManager.DeviceStateListener;
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -31,12 +31,14 @@
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.concurrent.Executor;
 
 /**
  * Provides communication with the device state system service on behalf of applications.
  *
  * @see DeviceStateManager
+ *
  * @hide
  */
 @VisibleForTesting(visibility = Visibility.PACKAGE)
@@ -68,13 +70,13 @@
     private DeviceStateManagerCallback mCallback;
 
     @GuardedBy("mLock")
-    private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>();
+    private final ArrayList<DeviceStateCallbackWrapper> mCallbacks = new ArrayList<>();
     @GuardedBy("mLock")
     private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>();
 
     @Nullable
     @GuardedBy("mLock")
-    private Integer mLastReceivedState;
+    private DeviceStateInfo mLastReceivedInfo;
 
     @VisibleForTesting
     public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
@@ -87,18 +89,31 @@
      * @see DeviceStateManager#getSupportedStates()
      */
     public int[] getSupportedStates() {
-        try {
-            return mDeviceStateManager.getSupportedDeviceStates();
-        } catch (RemoteException ex) {
-            throw ex.rethrowFromSystemServer();
+        synchronized (mLock) {
+            final DeviceStateInfo currentInfo;
+            if (mLastReceivedInfo != null) {
+                // If we have mLastReceivedInfo a callback is registered for this instance and it
+                // is receiving the most recent info from the server. Use that info here.
+                currentInfo = mLastReceivedInfo;
+            } else {
+                // If mLastReceivedInfo is null there is no registered callback so we manually
+                // fetch the current info.
+                try {
+                    currentInfo = mDeviceStateManager.getDeviceStateInfo();
+                } catch (RemoteException ex) {
+                    throw ex.rethrowFromSystemServer();
+                }
+            }
+
+            return Arrays.copyOf(currentInfo.supportedStates, currentInfo.supportedStates.length);
         }
     }
 
     /**
      * Submits a {@link DeviceStateRequest request} to modify the device state.
      *
-     * @see DeviceStateManager#requestState(DeviceStateRequest,
-     * Executor, DeviceStateRequest.Callback)
+     * @see DeviceStateManager#requestState(DeviceStateRequest, Executor,
+     * DeviceStateRequest.Callback)
      * @see DeviceStateRequest
      */
     public void requestState(@NonNull DeviceStateRequest request,
@@ -157,49 +172,56 @@
     }
 
     /**
-     * Registers a listener to receive notifications about changes in device state.
+     * Registers a callback to receive notifications about changes in device state.
      *
-     * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
+     * @see DeviceStateManager#registerCallback(Executor, DeviceStateCallback)
      */
     @VisibleForTesting(visibility = Visibility.PACKAGE)
-    public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
+    public void registerDeviceStateCallback(@NonNull DeviceStateCallback callback,
             @NonNull Executor executor) {
-        Integer stateToReport;
-        DeviceStateListenerWrapper wrapper;
+        DeviceStateCallbackWrapper wrapper;
+        DeviceStateInfo currentInfo;
         synchronized (mLock) {
-            registerCallbackIfNeededLocked();
-
-            int index = findListenerLocked(listener);
+            int index = findCallbackLocked(callback);
             if (index != -1) {
-                // This listener is already registered.
+                // This callback is already registered.
                 return;
             }
 
-            wrapper = new DeviceStateListenerWrapper(listener, executor);
-            mListeners.add(wrapper);
-            stateToReport = mLastReceivedState;
+            registerCallbackIfNeededLocked();
+            if (mLastReceivedInfo == null) {
+                // Initialize the last received info with the current info if this is the first
+                // callback being registered.
+                try {
+                    mLastReceivedInfo = mDeviceStateManager.getDeviceStateInfo();
+                } catch (RemoteException ex) {
+                    throw ex.rethrowFromSystemServer();
+                }
+            }
+
+            currentInfo = new DeviceStateInfo(mLastReceivedInfo);
+
+            wrapper = new DeviceStateCallbackWrapper(callback, executor);
+            mCallbacks.add(wrapper);
         }
 
-        if (stateToReport != null) {
-            // Notify the listener with the most recent device state from the server. If the state
-            // to report is null this is likely the first listener added and we're still waiting
-            // from the callback from the server.
-            wrapper.notifyDeviceStateChanged(stateToReport);
-        }
+        wrapper.notifySupportedStatesChanged(currentInfo.supportedStates);
+        wrapper.notifyBaseStateChanged(currentInfo.baseState);
+        wrapper.notifyStateChanged(currentInfo.currentState);
     }
 
     /**
-     * Unregisters a listener previously registered with
-     * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
+     * Unregisters a callback previously registered with
+     * {@link #registerDeviceStateCallback(DeviceStateCallback, Executor)}}.
      *
-     * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
+     * @see DeviceStateManager#unregisterCallback(DeviceStateCallback)
      */
     @VisibleForTesting(visibility = Visibility.PACKAGE)
-    public void unregisterDeviceStateListener(DeviceStateListener listener) {
+    public void unregisterDeviceStateCallback(@NonNull DeviceStateCallback callback) {
         synchronized (mLock) {
-            int indexToRemove = findListenerLocked(listener);
+            int indexToRemove = findCallbackLocked(callback);
             if (indexToRemove != -1) {
-                mListeners.remove(indexToRemove);
+                mCallbacks.remove(indexToRemove);
             }
         }
     }
@@ -210,14 +232,15 @@
             try {
                 mDeviceStateManager.registerCallback(mCallback);
             } catch (RemoteException ex) {
+                mCallback = null;
                 throw ex.rethrowFromSystemServer();
             }
         }
     }
 
-    private int findListenerLocked(DeviceStateListener listener) {
-        for (int i = 0; i < mListeners.size(); i++) {
-            if (mListeners.get(i).mDeviceStateListener.equals(listener)) {
+    private int findCallbackLocked(DeviceStateCallback callback) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            if (mCallbacks.get(i).mDeviceStateCallback.equals(callback)) {
                 return i;
             }
         }
@@ -234,16 +257,34 @@
         return null;
     }
 
-    /** Handles a call from the server that the device state has changed. */
-    private void handleDeviceStateChanged(int newDeviceState) {
-        ArrayList<DeviceStateListenerWrapper> listeners;
+    /** Handles a call from the server that the device state info has changed. */
+    private void handleDeviceStateInfoChanged(@NonNull DeviceStateInfo info) {
+        ArrayList<DeviceStateCallbackWrapper> callbacks;
+        DeviceStateInfo oldInfo;
         synchronized (mLock) {
-            mLastReceivedState = newDeviceState;
-            listeners = new ArrayList<>(mListeners);
+            oldInfo = mLastReceivedInfo;
+            mLastReceivedInfo = info;
+            callbacks = new ArrayList<>(mCallbacks);
         }
 
-        for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).notifyDeviceStateChanged(newDeviceState);
+        final int diff = oldInfo == null ? 1 : info.diff(oldInfo);
+        if ((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0) {
+            for (int i = 0; i < callbacks.size(); i++) {
+                // Copy the array to prevent callbacks from modifying the internal state.
+                final int[] supportedStates = Arrays.copyOf(info.supportedStates,
+                        info.supportedStates.length);
+                callbacks.get(i).notifySupportedStatesChanged(supportedStates);
+            }
+        }
+        if ((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0) {
+            for (int i = 0; i < callbacks.size(); i++) {
+                callbacks.get(i).notifyBaseStateChanged(info.baseState);
+            }
+        }
+        if ((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0) {
+            for (int i = 0; i < callbacks.size(); i++) {
+                callbacks.get(i).notifyStateChanged(info.currentState);
+            }
         }
     }
 
@@ -291,8 +332,8 @@
 
     private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
         @Override
-        public void onDeviceStateChanged(int deviceState) {
-            handleDeviceStateChanged(deviceState);
+        public void onDeviceStateInfoChanged(DeviceStateInfo info) {
+            handleDeviceStateInfoChanged(info);
         }
 
         @Override
@@ -311,17 +352,29 @@
         }
     }
 
-    private static final class DeviceStateListenerWrapper {
-        private final DeviceStateListener mDeviceStateListener;
+    private static final class DeviceStateCallbackWrapper {
+        @NonNull
+        private final DeviceStateCallback mDeviceStateCallback;
+        @NonNull
         private final Executor mExecutor;
 
-        DeviceStateListenerWrapper(DeviceStateListener listener, Executor executor) {
-            mDeviceStateListener = listener;
+        DeviceStateCallbackWrapper(@NonNull DeviceStateCallback callback,
+                @NonNull Executor executor) {
+            mDeviceStateCallback = callback;
             mExecutor = executor;
         }
 
-        void notifyDeviceStateChanged(int newDeviceState) {
-            mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState));
+        void notifySupportedStatesChanged(int[] newSupportedStates) {
+            mExecutor.execute(() ->
+                    mDeviceStateCallback.onSupportedStatesChanged(newSupportedStates));
+        }
+
+        void notifyBaseStateChanged(int newBaseState) {
+            mExecutor.execute(() -> mDeviceStateCallback.onBaseStateChanged(newBaseState));
+        }
+
+        void notifyStateChanged(int newDeviceState) {
+            mExecutor.execute(() -> mDeviceStateCallback.onStateChanged(newDeviceState));
         }
     }
 
diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java
index 70f7002..df488d2 100644
--- a/core/java/android/hardware/devicestate/DeviceStateRequest.java
+++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java
@@ -116,7 +116,7 @@
          * requested state.
          * <p>
          * Guaranteed to be called after a call to
-         * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)} with a state
+         * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} with a state
          * matching the requested state.
          */
         default void onRequestActivated(@NonNull DeviceStateRequest request) {}
@@ -125,7 +125,7 @@
          * Called to indicate the request has been temporarily suspended.
          * <p>
          * Guaranteed to be called before a call to
-         * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+         * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
          */
         default void onRequestSuspended(@NonNull DeviceStateRequest request) {}
 
@@ -135,7 +135,7 @@
          * DeviceStateRequest.Callback)}.
          * <p>
          * Guaranteed to be called before a call to
-         * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+         * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
          * <p>
          * Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to
          * occur before this method.
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index 323ad21..14ed03d 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -16,25 +16,26 @@
 
 package android.hardware.devicestate;
 
+import android.hardware.devicestate.DeviceStateInfo;
 import android.hardware.devicestate.IDeviceStateManagerCallback;
 
 /** @hide */
 interface IDeviceStateManager {
+    /** Returns the current device state info. */
+    DeviceStateInfo getDeviceStateInfo();
+
     /**
      * Registers a callback to receive notifications from the device state manager. Only one
      * callback can be registered per-process.
      * <p>
      * As the callback mechanism is used to alert the caller of changes to request status a callback
      * <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or
-     * {@link #cancelRequest(IBinder)}. Otherwise an exception will be thrown.
+     * {@link #cancelRequest(IBinder)}, otherwise an exception will be thrown.
      *
      * @throws SecurityException if a callback is already registered for the calling process.
      */
     void registerCallback(in IDeviceStateManagerCallback callback);
 
-    /** Returns the array of supported device state identifiers. */
-    int[] getSupportedDeviceStates();
-
     /**
      * Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been
      * previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
index ee2a071..593be86 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
@@ -16,20 +16,23 @@
 
 package android.hardware.devicestate;
 
+import android.hardware.devicestate.DeviceStateInfo;
+
 /** @hide */
 interface IDeviceStateManagerCallback {
     /**
-     * Called in response to a change in device state. Guaranteed to be called once with the initial
-     * value on registration of the callback.
+     * Called in response to a change in {@link DeviceStateInfo}.
      *
-     * @param deviceState the new state of the device.
+     * @param info the new device state info.
+     *
+     * @see DeviceStateInfo
      */
-    oneway void onDeviceStateChanged(int deviceState);
+    oneway void onDeviceStateInfoChanged(in DeviceStateInfo info);
 
     /**
      * Called to notify the callback that a request has become active. Guaranteed to be called
-     * after a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming active
-     * resulted in a device state change.
+     * after a subsequent call to {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request
+     * becoming active resulted in a change of device state info.
      *
      * @param token the request token previously registered with
      *        {@link IDeviceStateManager#requestState(IBinder, int, int)}
@@ -38,8 +41,8 @@
 
     /**
      * Called to notify the callback that a request has become suspended. Guaranteed to be called
-     * before a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming
-     * suspended resulted in a device state change.
+     * before a subsequent call to {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request
+     * becoming suspended resulted in a change of device state info.
      *
      * @param token the request token previously registered with
      *        {@link IDeviceStateManager#requestState(IBinder, int, int)}
@@ -49,8 +52,8 @@
     /**
      * Called to notify the callback that a request has become canceled. No further callbacks will
      * be triggered for this request. Guaranteed to be called before a subsequent call to
-     * {@link #onDeviceStateChanged(int)} if the request becoming canceled resulted in a device
-     * state change.
+     * {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request becoming canceled resulted
+     * in a change of device state info.
      *
      * @param token the request token previously registered with
      *        {@link IDeviceStateManager#requestState(IBinder, int, int)}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 09d0af1..617220e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10512,18 +10512,6 @@
                 "force_desktop_mode_on_external_displays";
 
         /**
-         * Whether to allow non-resizable apps to be freeform.
-         *
-         * TODO(b/176061101) remove after update all usages
-         * @deprecated use {@link #DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}
-         * @hide
-         */
-        @Deprecated
-        @Readable
-        public static final String DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM =
-                "enable_sizecompat_freeform";
-
-        /**
          * Whether to allow non-resizable apps to be shown in multi-window. The app will be
          * letterboxed if the request orientation is not met, and will be shown in size-compat
          * mode if the container size has changed.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index ec7e4c1..9688c67 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -544,6 +544,7 @@
         // recreate this Surface, so only release it when we are fully
         // detached.
         if (mSurfacePackage != null) {
+            mTmpTransaction.reparent(mSurfacePackage.getSurfaceControl(), null).apply();
             mSurfacePackage.release();
             mSurfacePackage = null;
         }
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 8d193bf..0e0f98e 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -861,6 +861,23 @@
     return jStatus;
 }
 
+static void android_media_AudioRecord_setLogSessionId(JNIEnv *env, jobject thiz,
+                                                      jstring jlogSessionId) {
+    sp<AudioRecord> record = getAudioRecord(env, thiz);
+    if (record == nullptr) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Unable to retrieve AudioRecord pointer for setLogSessionId()");
+    }
+    if (jlogSessionId == nullptr) {
+        ALOGV("%s: logSessionId nullptr", __func__);
+        record->setLogSessionId(nullptr);
+        return;
+    }
+    ScopedUtfChars logSessionId(env, jlogSessionId);
+    ALOGV("%s: logSessionId '%s'", __func__, logSessionId.c_str());
+    record->setLogSessionId(logSessionId.c_str());
+}
+
 // ----------------------------------------------------------------------------
 static jint android_media_AudioRecord_get_port_id(JNIEnv *env,  jobject thiz) {
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
@@ -876,50 +893,48 @@
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
-    // name,               signature,  funcPtr
-    {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
-    {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
-    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
-                                      (void *)android_media_AudioRecord_setup},
-    {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
-    {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
-    {"native_read_in_byte_array",
-                             "([BIIZ)I",
-                                     (void *)android_media_AudioRecord_readInArray<jbyteArray>},
-    {"native_read_in_short_array",
-                             "([SIIZ)I",
-                                     (void *)android_media_AudioRecord_readInArray<jshortArray>},
-    {"native_read_in_float_array",
-                             "([FIIZ)I",
-                                     (void *)android_media_AudioRecord_readInArray<jfloatArray>},
-    {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
-                                       (void *)android_media_AudioRecord_readInDirectBuffer},
-    {"native_get_buffer_size_in_frames",
-                             "()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames},
-    {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
-    {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
-    {"native_set_pos_update_period",
-                             "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
-    {"native_get_pos_update_period",
-                             "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
-    {"native_get_min_buff_size",
-                             "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
-    {"native_getMetrics",    "()Landroid/os/PersistableBundle;",
-                                         (void *)android_media_AudioRecord_native_getMetrics},
-    {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
-    {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
-    {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},
-    {"native_disableDeviceCallback", "()V",
-                                        (void *)android_media_AudioRecord_disableDeviceCallback},
-    {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
-                                       (void *)android_media_AudioRecord_get_timestamp},
-    {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
-                                        (void *)android_media_AudioRecord_get_active_microphones},
-    {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
-    {"native_set_preferred_microphone_direction", "(I)I",
-                        (void *)android_media_AudioRecord_set_preferred_microphone_direction},
-    {"native_set_preferred_microphone_field_dimension", "(F)I",
-                        (void *)android_media_AudioRecord_set_preferred_microphone_field_dimension},
+        {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
+        {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
+        {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
+         (void *)android_media_AudioRecord_setup},
+        {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
+        {"native_release", "()V", (void *)android_media_AudioRecord_release},
+        {"native_read_in_byte_array", "([BIIZ)I",
+         (void *)android_media_AudioRecord_readInArray<jbyteArray>},
+        {"native_read_in_short_array", "([SIIZ)I",
+         (void *)android_media_AudioRecord_readInArray<jshortArray>},
+        {"native_read_in_float_array", "([FIIZ)I",
+         (void *)android_media_AudioRecord_readInArray<jfloatArray>},
+        {"native_read_in_direct_buffer", "(Ljava/lang/Object;IZ)I",
+         (void *)android_media_AudioRecord_readInDirectBuffer},
+        {"native_get_buffer_size_in_frames", "()I",
+         (void *)android_media_AudioRecord_get_buffer_size_in_frames},
+        {"native_set_marker_pos", "(I)I", (void *)android_media_AudioRecord_set_marker_pos},
+        {"native_get_marker_pos", "()I", (void *)android_media_AudioRecord_get_marker_pos},
+        {"native_set_pos_update_period", "(I)I",
+         (void *)android_media_AudioRecord_set_pos_update_period},
+        {"native_get_pos_update_period", "()I",
+         (void *)android_media_AudioRecord_get_pos_update_period},
+        {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
+        {"native_getMetrics", "()Landroid/os/PersistableBundle;",
+         (void *)android_media_AudioRecord_native_getMetrics},
+        {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
+        {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
+        {"native_enableDeviceCallback", "()V",
+         (void *)android_media_AudioRecord_enableDeviceCallback},
+        {"native_disableDeviceCallback", "()V",
+         (void *)android_media_AudioRecord_disableDeviceCallback},
+        {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
+         (void *)android_media_AudioRecord_get_timestamp},
+        {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
+         (void *)android_media_AudioRecord_get_active_microphones},
+        {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
+        {"native_set_preferred_microphone_direction", "(I)I",
+         (void *)android_media_AudioRecord_set_preferred_microphone_direction},
+        {"native_set_preferred_microphone_field_dimension", "(F)I",
+         (void *)android_media_AudioRecord_set_preferred_microphone_field_dimension},
+        {"native_setLogSessionId", "(Ljava/lang/String;)V",
+         (void *)android_media_AudioRecord_setLogSessionId},
 };
 
 // field names found in android/media/AudioRecord.java
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index da60a75..cae6db5 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1426,8 +1426,13 @@
         jniThrowException(env, "java/lang/IllegalStateException",
                           "Unable to retrieve AudioTrack pointer for setLogSessionId()");
     }
+    if (jlogSessionId == nullptr) {
+        ALOGV("%s: logSessionId nullptr", __func__);
+        track->setLogSessionId(nullptr);
+        return;
+    }
     ScopedUtfChars logSessionId(env, jlogSessionId);
-    ALOGV("%s: logSessionId %s", __func__, logSessionId.c_str());
+    ALOGV("%s: logSessionId '%s'", __func__, logSessionId.c_str());
     track->setLogSessionId(logSessionId.c_str());
 }
 
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index c580b34..ad1d252 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -282,7 +282,8 @@
         optional SettingProto force_rtl = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto emulate_display_cutout = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto force_desktop_mode_on_external_displays = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto enable_sizecompat_freeform = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Deprecated, use enable_non_resizable_multi_window
+        optional SettingProto enable_sizecompat_freeform = 7 [ (android.privacy).dest = DEST_AUTOMATIC, deprecated = true ];
         optional SettingProto enable_non_resizable_multi_window = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Development development = 39;
diff --git a/core/tests/devicestatetests/Android.bp b/core/tests/devicestatetests/Android.bp
index f7b5932..7748de5 100644
--- a/core/tests/devicestatetests/Android.bp
+++ b/core/tests/devicestatetests/Android.bp
@@ -28,6 +28,7 @@
     static_libs: [
         "androidx.test.rules",
         "frameworks-base-testutils",
+        "mockito-target-minus-junit4",
     ],
     libs: ["android.test.runner"],
     platform_apis: true,
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java
new file mode 100644
index 0000000..dcef0a7
--- /dev/null
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.devicestate;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Unit tests for {@link DeviceStateInfo}.
+ * <p/>
+ * Run with <code>atest DeviceStateInfoTest</code>.
+ */
+@RunWith(JUnit4.class)
+@SmallTest
+public final class DeviceStateInfoTest {
+    @Test
+    public void create() {
+        final int[] supportedStates = new int[] { 0, 1, 2 };
+        final int baseState = 0;
+        final int currentState = 2;
+
+        final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState);
+        assertNotNull(info.supportedStates);
+        assertEquals(supportedStates, info.supportedStates);
+        assertEquals(baseState, info.baseState);
+        assertEquals(currentState, info.currentState);
+    }
+
+    @Test
+    public void equals() {
+        final int[] supportedStates = new int[] { 0, 1, 2 };
+        final int baseState = 0;
+        final int currentState = 2;
+
+        final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState);
+        assertTrue(info.equals(info));
+
+        final DeviceStateInfo sameInfo = new DeviceStateInfo(supportedStates, baseState,
+                currentState);
+        assertTrue(info.equals(sameInfo));
+
+        final DeviceStateInfo differentInfo = new DeviceStateInfo(new int[]{ 0, 2}, baseState,
+                currentState);
+        assertFalse(info.equals(differentInfo));
+    }
+
+    @Test
+    public void diff_sameObject() {
+        final int[] supportedStates = new int[] { 0, 1, 2 };
+        final int baseState = 0;
+        final int currentState = 2;
+
+        final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState);
+        assertEquals(0, info.diff(info));
+    }
+
+    @Test
+    public void diff_differentSupportedStates() {
+        final DeviceStateInfo info = new DeviceStateInfo(new int[] { 1 }, 0, 0);
+        final DeviceStateInfo otherInfo = new DeviceStateInfo(new int[] { 2 }, 0, 0);
+        final int diff = info.diff(otherInfo);
+        assertTrue((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0);
+        assertFalse((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0);
+        assertFalse((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0);
+    }
+
+    @Test
+    public void diff_differentNonOverrideState() {
+        final DeviceStateInfo info = new DeviceStateInfo(new int[] { 1 }, 1, 0);
+        final DeviceStateInfo otherInfo = new DeviceStateInfo(new int[] { 1 }, 2, 0);
+        final int diff = info.diff(otherInfo);
+        assertFalse((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0);
+        assertTrue((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0);
+        assertFalse((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0);
+    }
+
+    @Test
+    public void diff_differentState() {
+        final DeviceStateInfo info = new DeviceStateInfo(new int[] { 1 }, 0, 1);
+        final DeviceStateInfo otherInfo = new DeviceStateInfo(new int[] { 1 }, 0, 2);
+        final int diff = info.diff(otherInfo);
+        assertFalse((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0);
+        assertFalse((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0);
+        assertTrue((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0);
+    }
+
+    @Test
+    public void writeToParcel() {
+        final int[] supportedStates = new int[] { 0, 1, 2 };
+        final int nonOverrideState = 0;
+        final int state = 2;
+        final DeviceStateInfo originalInfo =
+                new DeviceStateInfo(supportedStates, nonOverrideState, state);
+
+        final Parcel parcel = Parcel.obtain();
+        originalInfo.writeToParcel(parcel, 0 /* flags */);
+        parcel.setDataPosition(0);
+
+        final DeviceStateInfo info = DeviceStateInfo.CREATOR.createFromParcel(parcel);
+        assertEquals(originalInfo, info);
+    }
+}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index e7fdfb8..79b4d8b 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -16,11 +16,15 @@
 
 package android.hardware.devicestate;
 
-import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
-import android.annotation.Nullable;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
 import android.os.IBinder;
 import android.os.RemoteException;
 
@@ -32,6 +36,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -58,42 +63,75 @@
     }
 
     @Test
-    public void registerListener() {
-        mService.setBaseState(DEFAULT_DEVICE_STATE);
+    public void registerCallback() {
+        DeviceStateCallback callback1 = mock(DeviceStateCallback.class);
+        DeviceStateCallback callback2 = mock(DeviceStateCallback.class);
 
-        TestDeviceStateListener listener1 = new TestDeviceStateListener();
-        TestDeviceStateListener listener2 = new TestDeviceStateListener();
-
-        mDeviceStateManagerGlobal.registerDeviceStateListener(listener1,
+        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1,
                 ConcurrentUtils.DIRECT_EXECUTOR);
-        mDeviceStateManagerGlobal.registerDeviceStateListener(listener2,
+        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2,
                 ConcurrentUtils.DIRECT_EXECUTOR);
-        assertEquals(DEFAULT_DEVICE_STATE, listener1.getLastReportedState().intValue());
-        assertEquals(DEFAULT_DEVICE_STATE, listener2.getLastReportedState().intValue());
 
+        // Verify initial callbacks
+        verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedStates()));
+        verify(callback1).onBaseStateChanged(eq(mService.getBaseState()));
+        verify(callback1).onStateChanged(eq(mService.getMergedState()));
+        verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates()));
+        verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
+        verify(callback2).onStateChanged(eq(mService.getMergedState()));
+
+        Mockito.reset(callback1);
+        Mockito.reset(callback2);
+
+        // Change the supported states and verify callback
+        mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE });
+        verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedStates()));
+        verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates()));
+        mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE });
+
+        Mockito.reset(callback1);
+        Mockito.reset(callback2);
+
+        // Change the base state and verify callback
         mService.setBaseState(OTHER_DEVICE_STATE);
-        assertEquals(OTHER_DEVICE_STATE, listener1.getLastReportedState().intValue());
-        assertEquals(OTHER_DEVICE_STATE, listener2.getLastReportedState().intValue());
+        verify(callback1).onBaseStateChanged(eq(mService.getBaseState()));
+        verify(callback1).onStateChanged(eq(mService.getMergedState()));
+        verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
+        verify(callback2).onStateChanged(eq(mService.getMergedState()));
+
+        Mockito.reset(callback1);
+        Mockito.reset(callback2);
+
+        // Change the requested state and verify callback
+        DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
+        mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+
+        verify(callback1).onStateChanged(eq(mService.getMergedState()));
+        verify(callback2).onStateChanged(eq(mService.getMergedState()));
     }
 
     @Test
-    public void unregisterListener() {
-        mService.setBaseState(DEFAULT_DEVICE_STATE);
+    public void unregisterCallback() {
+        DeviceStateCallback callback = mock(DeviceStateCallback.class);
 
-        TestDeviceStateListener listener = new TestDeviceStateListener();
-
-        mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
+        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
                 ConcurrentUtils.DIRECT_EXECUTOR);
-        assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
 
-        mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener);
+        // Verify initial callbacks
+        verify(callback).onSupportedStatesChanged(eq(mService.getSupportedStates()));
+        verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
+        verify(callback).onStateChanged(eq(mService.getMergedState()));
+        Mockito.reset(callback);
 
+        mDeviceStateManagerGlobal.unregisterDeviceStateCallback(callback);
+
+        mService.setSupportedStates(new int[]{OTHER_DEVICE_STATE});
         mService.setBaseState(OTHER_DEVICE_STATE);
-        assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+        verifyZeroInteractions(callback);
     }
 
     @Test
-    public void submittingRequestRegisteredCallback() {
+    public void submittingRequestRegistersCallback() {
         assertTrue(mService.mCallbacks.isEmpty());
 
         DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
@@ -104,37 +142,22 @@
 
     @Test
     public void submitRequest() {
-        mService.setBaseState(DEFAULT_DEVICE_STATE);
-
-        TestDeviceStateListener listener = new TestDeviceStateListener();
-        mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
+        DeviceStateCallback callback = mock(DeviceStateCallback.class);
+        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
                 ConcurrentUtils.DIRECT_EXECUTOR);
 
-        assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+        verify(callback).onStateChanged(eq(mService.getBaseState()));
+        Mockito.reset(callback);
 
         DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
         mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
 
-        assertEquals(OTHER_DEVICE_STATE, listener.getLastReportedState().intValue());
+        verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
+        Mockito.reset(callback);
 
         mDeviceStateManagerGlobal.cancelRequest(request);
 
-        assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
-    }
-
-    private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener {
-        @Nullable
-        private Integer mLastReportedDeviceState;
-
-        @Override
-        public void onDeviceStateChanged(int deviceState) {
-            mLastReportedDeviceState = deviceState;
-        }
-
-        @Nullable
-        public Integer getLastReportedState() {
-            return mLastReportedDeviceState;
-        }
+        verify(callback).onStateChanged(eq(mService.getBaseState()));
     }
 
     private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
@@ -150,12 +173,34 @@
             }
         }
 
+        private int[] mSupportedStates = new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
         private int mBaseState = DEFAULT_DEVICE_STATE;
-        private int mMergedState = DEFAULT_DEVICE_STATE;
         private ArrayList<Request> mRequests = new ArrayList<>();
 
         private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
 
+        private DeviceStateInfo getInfo() {
+            final int mergedState = mRequests.isEmpty()
+                    ? mBaseState : mRequests.get(mRequests.size() - 1).state;
+            return new DeviceStateInfo(mSupportedStates, mBaseState, mergedState);
+        }
+
+        private void notifyDeviceStateInfoChanged() {
+            final DeviceStateInfo info = getInfo();
+            for (IDeviceStateManagerCallback callback : mCallbacks) {
+                try {
+                    callback.onDeviceStateInfoChanged(info);
+                } catch (RemoteException e) {
+                    // Do nothing. Should never happen.
+                }
+            }
+        }
+
+        @Override
+        public DeviceStateInfo getDeviceStateInfo() {
+            return getInfo();
+        }
+
         @Override
         public void registerCallback(IDeviceStateManagerCallback callback) {
             if (mCallbacks.contains(callback)) {
@@ -163,16 +208,6 @@
             }
 
             mCallbacks.add(callback);
-            try {
-                callback.onDeviceStateChanged(mMergedState);
-            } catch (RemoteException e) {
-                // Do nothing. Should never happen.
-            }
-        }
-
-        @Override
-        public int[] getSupportedDeviceStates() {
-            return new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
         }
 
         @Override
@@ -190,7 +225,7 @@
 
             final Request request = new Request(token, state, flags);
             mRequests.add(request);
-            notifyStateChangedIfNeeded();
+            notifyDeviceStateInfoChanged();
 
             for (IDeviceStateManagerCallback callback : mCallbacks) {
                 try {
@@ -223,32 +258,29 @@
                     // Do nothing. Should never happen.
                 }
             }
-            notifyStateChangedIfNeeded();
+            notifyDeviceStateInfoChanged();
+        }
+
+        public void setSupportedStates(int[] states) {
+            mSupportedStates = states;
+            notifyDeviceStateInfoChanged();
+        }
+
+        public int[] getSupportedStates() {
+            return mSupportedStates;
         }
 
         public void setBaseState(int state) {
             mBaseState = state;
-            notifyStateChangedIfNeeded();
+            notifyDeviceStateInfoChanged();
         }
 
-        private void notifyStateChangedIfNeeded() {
-            final int originalMergedState = mMergedState;
+        public int getBaseState() {
+            return mBaseState;
+        }
 
-            if (!mRequests.isEmpty()) {
-                mMergedState = mRequests.get(mRequests.size() - 1).state;
-            } else {
-                mMergedState = mBaseState;
-            }
-
-            if (mMergedState != originalMergedState) {
-                for (IDeviceStateManagerCallback callback : mCallbacks) {
-                    try {
-                        callback.onDeviceStateChanged(mMergedState);
-                    } catch (RemoteException e) {
-                        // Do nothing. Should never happen.
-                    }
-                }
-            }
+        public int getMergedState() {
+            return getInfo().currentState;
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 5992447..46884fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -157,6 +157,7 @@
         });
         options.setLaunchCookie(launchCookie);
         options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        options.setRemoveWithTaskOrganizer(true);
     }
 
     /**
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index b196437..bf04b66 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -275,6 +275,12 @@
     private AudioAttributes mAudioAttributes;
     private boolean mIsSubmixFullVolume = false;
 
+    /**
+     * The log session id used for metrics.
+     * A null or empty string here means it is not set.
+     */
+    private String mLogSessionId;
+
     //---------------------------------------------------------
     // Constructor, Finalize
     //--------------------
@@ -1913,6 +1919,25 @@
         return native_set_preferred_microphone_field_dimension(zoom) == AudioSystem.SUCCESS;
     }
 
+    /**
+     * Sets a string handle to this AudioRecord for metrics collection.
+     *
+     * @param logSessionId a string which is used to identify this object
+     *        to the metrics service.  Proper generated Ids must be obtained
+     *        from the Java metrics service and should be considered opaque.
+     *        Use null to remove the logSessionId association.
+     * @throws IllegalStateException if AudioRecord not initialized.
+     *
+     * @hide
+     */
+    public void setLogSessionId(@Nullable String logSessionId) {
+        if (mState == STATE_UNINITIALIZED) {
+            throw new IllegalStateException("AudioRecord not initialized");
+        }
+        native_setLogSessionId(logSessionId);
+        mLogSessionId = logSessionId;
+    }
+
     //---------------------------------------------------------
     // Interface definitions
     //--------------------
@@ -2072,6 +2097,8 @@
     private native int native_set_preferred_microphone_direction(int direction);
     private native int native_set_preferred_microphone_field_dimension(float zoom);
 
+    private native void native_setLogSessionId(@Nullable String logSessionId);
+
     //---------------------------------------------------------
     // Utility methods
     //------------------
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 58174a0..04eaf07 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -567,6 +567,7 @@
 
     /**
      * The log session id used for metrics.
+     * A null or empty string here means it is not set.
      */
     private String mLogSessionId;
 
@@ -3978,12 +3979,14 @@
      * Sets a string handle to this AudioTrack for metrics collection.
      *
      * @param logSessionId a string which is used to identify this object
-     *        to the metrics service.
+     *        to the metrics service.  Proper generated Ids must be obtained
+     *        from the Java metrics service and should be considered opaque.
+     *        Use null to remove the logSessionId association.
      * @throws IllegalStateException if AudioTrack not initialized.
      *
      * @hide
      */
-    public void setLogSessionId(@NonNull String logSessionId) {
+    public void setLogSessionId(@Nullable String logSessionId) {
         if (mState == STATE_UNINITIALIZED) {
             throw new IllegalStateException("track not initialized");
         }
@@ -4226,7 +4229,7 @@
     private native int native_get_audio_description_mix_level_db(float[] level);
     private native int native_set_dual_mono_mode(int dualMonoMode);
     private native int native_get_dual_mono_mode(int[] dualMonoMode);
-    private native void native_setLogSessionId(@NonNull String logSessionId);
+    private native void native_setLogSessionId(@Nullable String logSessionId);
 
     /**
      * Sets the audio service Player Interface Id.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1ce738e..a0b9528 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -551,9 +551,6 @@
                 Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
                 GlobalSettingsProto.Development.FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS);
         dumpSetting(s, p,
-                Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
-                GlobalSettingsProto.Development.ENABLE_SIZECOMPAT_FREEFORM);
-        dumpSetting(s, p,
                 Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
                 GlobalSettingsProto.Development.ENABLE_NON_RESIZABLE_MULTI_WINDOW);
         p.end(developmentToken);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 303e5bb..4dc6d14 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -227,7 +227,6 @@
                     Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
                     Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
                     Settings.Global.DEVELOPMENT_FORCE_RTL,
-                    Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
                     Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
                     Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
                     Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV,
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e5ef935..d998ebb 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3841,6 +3841,8 @@
                     r.name.getClassName());
             stopServiceAndUpdateAllowlistManagerLocked(r);
             if (r.app.getThread() != null) {
+                // Bump the process to the top of LRU list
+                mAm.updateLruProcessLocked(r.app, false, null);
                 updateServiceForegroundLocked(r.app.mServices, false);
                 try {
                     bumpServiceExecutingLocked(r, false, "destroy");
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index b3a6f26..835b468 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -25,6 +25,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.devicestate.DeviceStateInfo;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.IDeviceStateManager;
 import android.hardware.devicestate.IDeviceStateManagerCallback;
@@ -47,6 +48,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Optional;
 
 /**
@@ -76,6 +78,9 @@
 public final class DeviceStateManagerService extends SystemService {
     private static final String TAG = "DeviceStateManagerService";
     private static final boolean DEBUG = false;
+    // The device state to use as a placeholder before callback from the DeviceStateProvider occurs.
+    private static final DeviceState UNSPECIFIED_DEVICE_STATE =
+            new DeviceState(MINIMUM_DEVICE_STATE, "UNSPECIFIED");
 
     private final Object mLock = new Object();
     @NonNull
@@ -87,11 +92,11 @@
     @GuardedBy("mLock")
     private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
 
-    // The current committed device state. The default of UNSET will be replaced by
-    // the current state after the initial callback from the DeviceStateProvider.
+    // The current committed device state. The default of UNSPECIFIED_DEVICE_STATE will be replaced
+    // by the current state after the initial callback from the DeviceStateProvider.
     @GuardedBy("mLock")
     @NonNull
-    private DeviceState mCommittedState = new DeviceState(MINIMUM_DEVICE_STATE, "UNSET");
+    private DeviceState mCommittedState = UNSPECIFIED_DEVICE_STATE;
     // The device state that is currently awaiting callback from the policy to be committed.
     @GuardedBy("mLock")
     @NonNull
@@ -103,7 +108,7 @@
     // The device state that is set by the device state provider.
     @GuardedBy("mLock")
     @NonNull
-    private Optional<DeviceState> mBaseState = Optional.empty();
+    private DeviceState mBaseState = UNSPECIFIED_DEVICE_STATE;
 
     // List of processes registered to receive notifications about changes to device state and
     // request status indexed by process id.
@@ -166,7 +171,7 @@
      * @see #getOverrideState()
      */
     @NonNull
-    Optional<DeviceState> getBaseState() {
+    DeviceState getBaseState() {
         synchronized (mLock) {
             return mBaseState;
         }
@@ -203,33 +208,47 @@
     /** Returns the list of currently supported device state identifiers. */
     private int[] getSupportedStateIdentifiers() {
         synchronized (mLock) {
-            int[] supportedStates = new int[mDeviceStates.size()];
-            for (int i = 0; i < supportedStates.length; i++) {
-                supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier();
-            }
-            return supportedStates;
+            return getSupportedStateIdentifiersLocked();
         }
     }
 
+    /** Returns the list of currently supported device state identifiers. */
+    private int[] getSupportedStateIdentifiersLocked() {
+        int[] supportedStates = new int[mDeviceStates.size()];
+        for (int i = 0; i < supportedStates.length; i++) {
+            supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier();
+        }
+        return supportedStates;
+    }
+
+    @NonNull
+    private DeviceStateInfo getDeviceStateInfoLocked() {
+        final int[] supportedStates = getSupportedStateIdentifiersLocked();
+        final int baseState = mBaseState.getIdentifier();
+        final int currentState = mCommittedState.getIdentifier();
+
+        return new DeviceStateInfo(supportedStates, baseState, currentState);
+    }
+
     @VisibleForTesting
     IDeviceStateManager getBinderService() {
         return mBinderService;
     }
 
     private void updateSupportedStates(DeviceState[] supportedDeviceStates) {
+        boolean updatedPendingState;
         synchronized (mLock) {
+            final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked();
+
             mDeviceStates.clear();
             for (int i = 0; i < supportedDeviceStates.length; i++) {
                 DeviceState state = supportedDeviceStates[i];
                 mDeviceStates.put(state.getIdentifier(), state);
             }
 
-            if (mBaseState.isPresent()
-                    && !isSupportedStateLocked(mBaseState.get().getIdentifier())) {
-                // The current base state is no longer valid. We'll clear it here, though
-                // we won't actually update the current state until a callback comes from the
-                // provider with the most recent state.
-                mBaseState = Optional.empty();
+            final int[] newStateIdentifiers = getSupportedStateIdentifiersLocked();
+            if (Arrays.equals(oldStateIdentifiers, newStateIdentifiers)) {
+                return;
             }
 
             final int requestSize = mRequestRecords.size();
@@ -240,7 +259,14 @@
                 }
             }
 
-            updatePendingStateLocked();
+            updatedPendingState = updatePendingStateLocked();
+        }
+
+        if (!updatedPendingState) {
+            // If the change in the supported states didn't result in a change of the pending state
+            // commitPendingState() will never be called and the callbacks will never be notified
+            // of the change.
+            notifyDeviceStateInfoChanged();
         }
 
         notifyRequestsOfStatusChangeIfNeeded();
@@ -265,23 +291,25 @@
     }
 
     /**
-     * Requests to set the base state. The request may not be honored under certain conditions, for
-     * example if the provided state is not supported.
+     * Sets the base state.
+     *
+     * @throws IllegalArgumentException if the {@code identifier} is not a supported state.
      *
      * @see #isSupportedStateLocked(int)
      */
     private void setBaseState(int identifier) {
+        boolean updatedPendingState;
         synchronized (mLock) {
-            if (mBaseState.isPresent() && mBaseState.get().getIdentifier() == identifier) {
-                // Base state hasn't changed. Nothing to do.
-                return;
-            }
-
-            final Optional<DeviceState> baseState = getStateLocked(identifier);
-            if (!baseState.isPresent()) {
+            final Optional<DeviceState> baseStateOptional = getStateLocked(identifier);
+            if (!baseStateOptional.isPresent()) {
                 throw new IllegalArgumentException("Base state is not supported");
             }
 
+            final DeviceState baseState = baseStateOptional.get();
+            if (mBaseState.equals(baseState)) {
+                // Base state hasn't changed. Nothing to do.
+                return;
+            }
             mBaseState = baseState;
 
             final int requestSize = mRequestRecords.size();
@@ -292,7 +320,14 @@
                 }
             }
 
-            updatePendingStateLocked();
+            updatedPendingState = updatePendingStateLocked();
+        }
+
+        if (!updatedPendingState) {
+            // If the change in base state didn't result in a change of the pending state
+            // commitPendingState() will never be called and the callbacks will never be notified
+            // of the change.
+            notifyDeviceStateInfoChanged();
         }
 
         notifyRequestsOfStatusChangeIfNeeded();
@@ -303,32 +338,39 @@
      * Tries to update the current pending state with the current requested state. Must call
      * {@link #notifyPolicyIfNeeded()} to actually notify the policy that the state is being
      * changed.
+     *
+     * @return {@code true} if the pending state has changed as a result of this call, {@code false}
+     * otherwise.
      */
-    private void updatePendingStateLocked() {
+    private boolean updatePendingStateLocked() {
         if (mPendingState.isPresent()) {
             // Have pending state, can not configure a new state until the state is committed.
-            return;
+            return false;
         }
 
         final DeviceState stateToConfigure;
         if (!mRequestRecords.isEmpty()) {
             stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState;
+        } else if (isSupportedStateLocked(mBaseState.getIdentifier())) {
+            // Base state could have recently become unsupported after a change in supported states.
+            stateToConfigure = mBaseState;
         } else {
-            stateToConfigure = mBaseState.orElse(null);
+            stateToConfigure = null;
         }
 
         if (stateToConfigure == null) {
             // No currently requested state.
-            return;
+            return false;
         }
 
-        if (stateToConfigure == mCommittedState) {
+        if (stateToConfigure.equals(mCommittedState)) {
             // The state requesting to be committed already matches the current committed state.
-            return;
+            return false;
         }
 
         mPendingState = Optional.of(stateToConfigure);
         mIsPolicyWaitingForState = true;
+        return true;
     }
 
     /**
@@ -374,13 +416,11 @@
      */
     private void commitPendingState() {
         // Update the current state.
-        int newState;
         synchronized (mLock) {
             if (DEBUG) {
                 Slog.d(TAG, "Committing state: " + mPendingState);
             }
             mCommittedState = mPendingState.get();
-            newState = mCommittedState.getIdentifier();
 
             if (!mRequestRecords.isEmpty()) {
                 final OverrideRequestRecord topRequest =
@@ -393,7 +433,7 @@
         }
 
         // Notify callbacks of a change.
-        notifyDeviceStateChanged(newState);
+        notifyDeviceStateInfoChanged();
 
         // Notify the top request that it's active.
         notifyRequestsOfStatusChangeIfNeeded();
@@ -402,14 +442,15 @@
         notifyPolicyIfNeeded();
     }
 
-    private void notifyDeviceStateChanged(int deviceState) {
+    private void notifyDeviceStateInfoChanged() {
         if (Thread.holdsLock(mLock)) {
             throw new IllegalStateException(
                     "Attempting to notify callbacks with service lock held.");
         }
 
-        // Grab the lock and copy the process records.
+        // Grab the lock and copy the process records and the current info.
         ArrayList<ProcessRecord> registeredProcesses;
+        DeviceStateInfo info;
         synchronized (mLock) {
             if (mProcessRecords.size() == 0) {
                 return;
@@ -419,11 +460,13 @@
             for (int i = 0; i < mProcessRecords.size(); i++) {
                 registeredProcesses.add(mProcessRecords.valueAt(i));
             }
+
+            info = getDeviceStateInfoLocked();
         }
 
         // After releasing the lock, send the notifications out.
         for (int i = 0; i < registeredProcesses.size(); i++) {
-            registeredProcesses.get(i).notifyDeviceStateAsync(deviceState);
+            registeredProcesses.get(i).notifyDeviceStateInfoAsync(info);
         }
     }
 
@@ -454,28 +497,20 @@
     }
 
     private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
-        int currentState;
-        ProcessRecord record;
-        // Grab the lock to register the callback and get the current state.
         synchronized (mLock) {
             if (mProcessRecords.contains(pid)) {
                 throw new SecurityException("The calling process has already registered an"
                         + " IDeviceStateManagerCallback.");
             }
 
-            record = new ProcessRecord(callback, pid);
+            ProcessRecord record = new ProcessRecord(callback, pid);
             try {
                 callback.asBinder().linkToDeath(record, 0);
             } catch (RemoteException ex) {
                 throw new RuntimeException(ex);
             }
-
             mProcessRecords.put(pid, record);
-            currentState = mCommittedState.getIdentifier();
         }
-
-        // Notify the callback of the state at registration.
-        record.notifyDeviceStateAsync(currentState);
     }
 
     private void handleProcessDied(ProcessRecord processRecord) {
@@ -626,9 +661,9 @@
             handleProcessDied(this);
         }
 
-        public void notifyDeviceStateAsync(int devicestate) {
+        public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) {
             try {
-                mCallback.onDeviceStateChanged(devicestate);
+                mCallback.onDeviceStateInfoChanged(info);
             } catch (RemoteException ex) {
                 Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.",
                         ex);
@@ -752,6 +787,13 @@
     /** Implementation of {@link IDeviceStateManager} published as a binder service. */
     private final class BinderService extends IDeviceStateManager.Stub {
         @Override // Binder call
+        public DeviceStateInfo getDeviceStateInfo() {
+            synchronized (mLock) {
+                return getDeviceStateInfoLocked();
+            }
+        }
+
+        @Override // Binder call
         public void registerCallback(IDeviceStateManagerCallback callback) {
             if (callback == null) {
                 throw new IllegalArgumentException("Device state callback must not be null.");
@@ -767,16 +809,6 @@
         }
 
         @Override // Binder call
-        public int[] getSupportedDeviceStates() {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                return getSupportedStateIdentifiers();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override // Binder call
         public void requestState(IBinder token, int state, int flags) {
             getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                     "Permission required to request device state.");
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index 6cc55a6..f346600 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -64,13 +64,13 @@
 
     private void printState(PrintWriter pw) {
         DeviceState committedState = mService.getCommittedState();
-        Optional<DeviceState> baseState = mService.getBaseState();
+        DeviceState baseState = mService.getBaseState();
         Optional<DeviceState> overrideState = mService.getOverrideState();
 
         pw.println("Committed state: " + committedState);
         if (overrideState.isPresent()) {
             pw.println("----------------------");
-            pw.println("Base state: " + baseState.orElse(null));
+            pw.println("Base state: " + baseState);
             pw.println("Override state: " + overrideState.get());
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c62dd72..52149ee 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -532,7 +532,7 @@
 
             DeviceStateManager deviceStateManager =
                     mContext.getSystemService(DeviceStateManager.class);
-            deviceStateManager.addDeviceStateListener(new HandlerExecutor(mHandler),
+            deviceStateManager.registerCallback(new HandlerExecutor(mHandler),
                     new DeviceStateListener());
 
             scheduleTraversalLocked(false);
@@ -2974,9 +2974,9 @@
     /**
      * Listens to changes in device state and reports the state to LogicalDisplayMapper.
      */
-    class DeviceStateListener implements DeviceStateManager.DeviceStateListener {
+    class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
         @Override
-        public void onDeviceStateChanged(int deviceState) {
+        public void onStateChanged(int deviceState) {
             synchronized (mSyncRoot) {
                 mLogicalDisplayMapper.setDeviceStateLocked(deviceState);
             }
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index 82fc22c..0e12584 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -74,7 +74,7 @@
         mHandler = handler;
 
         DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class);
-        deviceStateManager.addDeviceStateListener(new HandlerExecutor(handler),
+        deviceStateManager.registerCallback(new HandlerExecutor(handler),
                 new DeviceStateListener(context));
     }
 
@@ -208,7 +208,7 @@
      * matches the value in the {@link com.android.internal.R.integer.config_foldedDeviceState}
      * resource.
      */
-    private class DeviceStateListener implements DeviceStateManager.DeviceStateListener {
+    private class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
         private final int[] mFoldedDeviceStates;
 
         DeviceStateListener(Context context) {
@@ -217,7 +217,7 @@
         }
 
         @Override
-        public void onDeviceStateChanged(int deviceState) {
+        public void onStateChanged(int deviceState) {
             boolean folded = false;
             for (int i = 0; i < mFoldedDeviceStates.length; i++) {
                 if (deviceState == mFoldedDeviceStates[i]) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 151895f..c830ba9 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -808,7 +808,7 @@
 
                 if (rootTask.inFreeformWindowingMode()) {
                     rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                } else if (!mService.mSizeCompatFreeform && r.inSizeCompatMode()) {
+                } else if (!mService.mSupportsNonResizableMultiWindow && r.inSizeCompatMode()) {
                     throw new IllegalStateException("Size-compat windows are currently not"
                             + "freeform-enabled");
                 } else if (rootTask.getParent().inFreeformWindowingMode()) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f40f4a9..c1fe488 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2562,9 +2562,7 @@
      *         root task.
      */
     boolean supportsFreeform() {
-        return mAtmService.mSupportsFreeformWindowManagement
-                // Either the activity is resizable, or we allow size compat in freeform.
-                && (supportsMultiWindow() || mAtmService.mSizeCompatFreeform);
+        return mAtmService.mSupportsFreeformWindowManagement && supportsMultiWindow();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index af032c1..db49915 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -58,7 +58,6 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
 import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
@@ -557,7 +556,6 @@
     boolean mSupportsPictureInPicture;
     boolean mSupportsMultiDisplay;
     boolean mForceResizableActivities;
-    boolean mSizeCompatFreeform;
     boolean mSupportsNonResizableMultiWindow;
 
     final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
@@ -788,8 +786,6 @@
         final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
         final boolean forceResizable = Settings.Global.getInt(
                 resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
-        final boolean sizeCompatFreeform = Settings.Global.getInt(
-                resolver, DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
         final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(
                 resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0;
 
@@ -805,7 +801,6 @@
 
         synchronized (mGlobalLock) {
             mForceResizableActivities = forceResizable;
-            mSizeCompatFreeform = sizeCompatFreeform;
             mSupportsNonResizableMultiWindow = supportsNonResizableMultiWindow;
             final boolean multiWindowFormEnabled = freeformWindowManagement
                     || supportsSplitScreenMultiWindow
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7085156..8aa00d0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -835,6 +835,9 @@
     // Tracking cookie for the creation of this task.
     IBinder mLaunchCookie;
 
+    // The task will be removed when TaskOrganizer, which is managing the task, is destroyed.
+    boolean mRemoveWithTaskOrganizer;
+
     private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
             Intent _affinityIntent, String _affinity, String _rootAffinity,
             ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
@@ -846,7 +849,8 @@
             boolean supportsPictureInPicture, boolean _realActivitySuspended,
             boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info,
             IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
-            boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear) {
+            boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
+            boolean _removeWithTaskOrganizer) {
         super(atmService.mWindowManager);
 
         mAtmService = atmService;
@@ -905,6 +909,7 @@
         mCreatedByOrganizer = _createdByOrganizer;
         mLaunchCookie = _launchCookie;
         mDeferTaskAppear = _deferTaskAppear;
+        mRemoveWithTaskOrganizer = _removeWithTaskOrganizer;
         EventLogTags.writeWmTaskCreated(mTaskId, isRootTask() ? INVALID_TASK_ID : getRootTaskId());
     }
 
@@ -2836,8 +2841,6 @@
                     windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
             if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
                     && candidateWindowingMode != WINDOWING_MODE_PINNED
-                    && (candidateWindowingMode != WINDOWING_MODE_FREEFORM
-                            || !mTaskSupervisor.mService.mSizeCompatFreeform)
                     && !mTaskSupervisor.mService.mSupportsNonResizableMultiWindow) {
                 getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
                         WINDOWING_MODE_FULLSCREEN);
@@ -7845,6 +7848,7 @@
         private IBinder mLaunchCookie;
         private boolean mOnTop;
         private boolean mHasBeenVisible;
+        private boolean mRemoveWithTaskOrganizer;
 
         Builder(ActivityTaskManagerService atm) {
             mAtmService = atm;
@@ -8140,6 +8144,9 @@
             mCallingPackage = mActivityInfo.packageName;
             mResizeMode = mActivityInfo.resizeMode;
             mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture();
+            if (mActivityOptions != null) {
+                mRemoveWithTaskOrganizer = mActivityOptions.getRemoveWithTaskOranizer();
+            }
 
             final Task task = buildInner();
             task.mHasBeenVisible = mHasBeenVisible;
@@ -8178,7 +8185,7 @@
                     mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,
                     mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight,
                     mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer,
-                    mLaunchCookie, mDeferTaskAppear);
+                    mLaunchCookie, mDeferTaskAppear, mRemoveWithTaskOrganizer);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 82d9c21..f3b69e3 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -572,8 +572,7 @@
 
     private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
             TaskDisplayArea displayArea) {
-        // TODO(176061101): Migrate |mSizeCompatFreeform| to |mSupportsNonResizableMultiWindow|.
-        if (!mSupervisor.mService.mSizeCompatFreeform || activity.isResizeable()) {
+        if (!mSupervisor.mService.mSupportsNonResizableMultiWindow || activity.isResizeable()) {
             return false;
         }
         final DisplayContent display = displayArea.getDisplayContent();
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 1531e56..fc6db61 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -285,18 +285,20 @@
             return false;
         }
 
-        private boolean removeTask(Task t) {
+        private boolean removeTask(Task t, boolean removeFromSystem) {
             mOrganizedTasks.remove(t);
             mInterceptBackPressedOnRootTasks.remove(t.mTaskId);
-
-            if (t.mTaskAppearedSent) {
+            boolean taskAppearedSent = t.mTaskAppearedSent;
+            if (taskAppearedSent) {
                 if (t.getSurfaceControl() != null) {
                     t.migrateToNewSurfaceControl();
                 }
                 t.mTaskAppearedSent = false;
-                return true;
             }
-            return false;
+            if (removeFromSystem) {
+                mService.removeTask(t.mTaskId);
+            }
+            return taskAppearedSent;
         }
 
         void dispose() {
@@ -311,7 +313,7 @@
                 if (mOrganizedTasks.contains(t)) {
                     // updateTaskOrganizerState should remove the task from the list, but still
                     // check it again to avoid while-loop isn't terminate.
-                    if (removeTask(t)) {
+                    if (removeTask(t, t.mRemoveWithTaskOrganizer)) {
                         TaskOrganizerController.this.onTaskVanishedInternal(
                                 mOrganizer.mTaskOrganizer, t);
                     }
@@ -527,7 +529,7 @@
 
     void onTaskVanished(ITaskOrganizer organizer, Task task) {
         final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
-        if (state != null && state.removeTask(task)) {
+        if (state != null && state.removeTask(task, false /* removeFromSystem */)) {
             onTaskVanishedInternal(organizer, task);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c0ccd81..518176b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -39,7 +39,6 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
 import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR;
@@ -803,8 +802,6 @@
                 Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT);
         private final Uri mForceResizableUri = Settings.Global.getUriFor(
                 DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
-        private final Uri mSizeCompatFreeformUri = Settings.Global.getUriFor(
-                DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM);
         private final Uri mSupportsNonResizableMultiWindowUri = Settings.Global.getUriFor(
                 DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW);
         private final Uri mRenderShadowsInCompositorUri = Settings.Global.getUriFor(
@@ -831,8 +828,6 @@
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(mFreeformWindowUri, false, this, UserHandle.USER_ALL);
             resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL);
-            resolver.registerContentObserver(mSizeCompatFreeformUri, false, this,
-                    UserHandle.USER_ALL);
             resolver.registerContentObserver(mSupportsNonResizableMultiWindowUri, false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this,
@@ -872,11 +867,6 @@
                 return;
             }
 
-            if (mSizeCompatFreeformUri.equals(uri)) {
-                updateSizeCompatFreeform();
-                return;
-            }
-
             if (mSupportsNonResizableMultiWindowUri.equals(uri)) {
                 updateSupportsNonResizableMultiWindow();
                 return;
@@ -974,14 +964,6 @@
             mAtmService.mForceResizableActivities = forceResizable;
         }
 
-        void updateSizeCompatFreeform() {
-            ContentResolver resolver = mContext.getContentResolver();
-            final boolean sizeCompatFreeform = Settings.Global.getInt(resolver,
-                    DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
-
-            mAtmService.mSizeCompatFreeform = sizeCompatFreeform;
-        }
-
         void updateSupportsNonResizableMultiWindow() {
             ContentResolver resolver = mContext.getContentResolver();
             final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(resolver,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index bf95f4c..29db740 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1326,6 +1326,46 @@
     }
 
     @Test
+    public void testGetMaxJobExecutionTimeLocked_Regular_Active() {
+        JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked_Regular_Active", 0);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 2 * HOUR_IN_MILLIS);
+        setDischarging();
+        setStandbyBucket(ACTIVE_INDEX, job);
+        setProcessState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
+
+        // ACTIVE apps (where allowed time = window size) should be capped at max execution limit.
+        synchronized (mQuotaController.mLock) {
+            assertEquals(2 * HOUR_IN_MILLIS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+        }
+
+        // Make sure sessions are factored in properly.
+        mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
+                createTimingSession(sElapsedRealtimeClock.millis() - (6 * HOUR_IN_MILLIS),
+                        30 * MINUTE_IN_MILLIS, 1), false);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(90 * MINUTE_IN_MILLIS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+        }
+
+        mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
+                createTimingSession(sElapsedRealtimeClock.millis() - (5 * HOUR_IN_MILLIS),
+                        30 * MINUTE_IN_MILLIS, 1), false);
+        mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
+                createTimingSession(sElapsedRealtimeClock.millis() - (4 * HOUR_IN_MILLIS),
+                        30 * MINUTE_IN_MILLIS, 1), false);
+        mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
+                createTimingSession(sElapsedRealtimeClock.millis() - (3 * HOUR_IN_MILLIS),
+                        25 * MINUTE_IN_MILLIS, 1), false);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(5 * MINUTE_IN_MILLIS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+        }
+    }
+
+    @Test
     public void testGetMaxJobExecutionTimeLocked_EJ() {
         final long timeUsedMs = 3 * MINUTE_IN_MILLIS;
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index a078a77..26a549d 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -21,8 +21,10 @@
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertThrows;
 
+import android.hardware.devicestate.DeviceStateInfo;
 import android.hardware.devicestate.DeviceStateRequest;
 import android.hardware.devicestate.IDeviceStateManagerCallback;
 import android.os.Binder;
@@ -33,10 +35,13 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import junit.framework.Assert;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Optional;
 
@@ -70,14 +75,14 @@
     public void baseStateChanged() {
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
 
         mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), OTHER_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
     }
@@ -89,21 +94,21 @@
         mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), OTHER_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
 
         mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
 
         mPolicy.resumeConfigure();
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
     }
@@ -116,7 +121,7 @@
 
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
     }
@@ -129,16 +134,19 @@
 
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     @Test
-    public void supportedStatesChanged() {
+    public void supportedStatesChanged() throws RemoteException {
+        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        mService.getBinderService().registerCallback(callback);
+
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
 
         mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
 
@@ -146,27 +154,44 @@
         // supported.
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
+
+        assertArrayEquals(callback.getLastNotifiedInfo().supportedStates,
+                new int[] { DEFAULT_DEVICE_STATE.getIdentifier() });
     }
 
     @Test
-    public void supportedStatesChanged_unsupportedBaseState() {
+    public void supportedStatesChanged_statesRemainSame() throws RemoteException {
+        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        mService.getBinderService().registerCallback(callback);
+
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
 
-        mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE });
+        mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE,
+                OTHER_DEVICE_STATE });
 
-        // The current requested state is cleared because it is no longer supported.
+        // The current committed and requests states do not change because the current state remains
+        // supported.
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState(), Optional.empty());
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
 
-        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        // The callback wasn't notified about a change in supported states as the states have not
+        // changed.
+        assertNull(callback.getLastNotifiedInfo());
+    }
 
-        assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+    @Test
+    public void getDeviceStateInfo() throws RemoteException {
+        DeviceStateInfo info = mService.getBinderService().getDeviceStateInfo();
+        assertNotNull(info);
+        assertArrayEquals(info.supportedStates,
+                new int[] { DEFAULT_DEVICE_STATE.getIdentifier(),
+                        OTHER_DEVICE_STATE.getIdentifier() });
+        assertEquals(info.baseState, DEFAULT_DEVICE_STATE.getIdentifier());
+        assertEquals(info.currentState, DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     @Test
@@ -175,41 +200,33 @@
         mService.getBinderService().registerCallback(callback);
 
         mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
-        assertNotNull(callback.getLastNotifiedValue());
-        assertEquals(callback.getLastNotifiedValue().intValue(),
+        assertEquals(callback.getLastNotifiedInfo().baseState,
+                OTHER_DEVICE_STATE.getIdentifier());
+        assertEquals(callback.getLastNotifiedInfo().currentState,
                 OTHER_DEVICE_STATE.getIdentifier());
 
         mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
-        assertEquals(callback.getLastNotifiedValue().intValue(),
+        assertEquals(callback.getLastNotifiedInfo().baseState,
+                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertEquals(callback.getLastNotifiedInfo().currentState,
                 DEFAULT_DEVICE_STATE.getIdentifier());
 
         mPolicy.blockConfigure();
         mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
         // The callback should not have been notified of the state change as the policy is still
         // pending callback.
-        assertEquals(callback.getLastNotifiedValue().intValue(),
+        assertEquals(callback.getLastNotifiedInfo().baseState,
+                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertEquals(callback.getLastNotifiedInfo().currentState,
                 DEFAULT_DEVICE_STATE.getIdentifier());
 
         mPolicy.resumeConfigure();
         // Now that the policy is finished processing the callback should be notified of the state
         // change.
-        assertEquals(callback.getLastNotifiedValue().intValue(),
+        assertEquals(callback.getLastNotifiedInfo().baseState,
                 OTHER_DEVICE_STATE.getIdentifier());
-    }
-
-    @Test
-    public void registerCallback_emitsInitialValue() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
-        mService.getBinderService().registerCallback(callback);
-        assertNotNull(callback.getLastNotifiedValue());
-        assertEquals(callback.getLastNotifiedValue().intValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
-    }
-
-    @Test
-    public void getSupportedDeviceStates() throws RemoteException {
-        final int[] expectedStates = new int[] { 0, 1 };
-        assertEquals(mService.getBinderService().getSupportedDeviceStates(), expectedStates);
+        assertEquals(callback.getLastNotifiedInfo().currentState,
+                OTHER_DEVICE_STATE.getIdentifier());
     }
 
     @Test
@@ -228,11 +245,16 @@
                 TestDeviceStateManagerCallback.STATUS_ACTIVE);
         // Committed state changes as there is a requested override.
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
 
+        assertNotNull(callback.getLastNotifiedInfo());
+        assertEquals(callback.getLastNotifiedInfo().baseState,
+                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertEquals(callback.getLastNotifiedInfo().currentState,
+                OTHER_DEVICE_STATE.getIdentifier());
 
         mService.getBinderService().cancelRequest(token);
 
@@ -240,10 +262,15 @@
                 TestDeviceStateManagerCallback.STATUS_CANCELED);
         // Committed state is set back to the requested state once the override is cleared.
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
         assertFalse(mService.getOverrideState().isPresent());
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
+
+        assertEquals(callback.getLastNotifiedInfo().baseState,
+                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertEquals(callback.getLastNotifiedInfo().currentState,
+                DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     @Test
@@ -263,7 +290,7 @@
 
         // Committed state changes as there is a requested override.
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
@@ -275,7 +302,7 @@
                 TestDeviceStateManagerCallback.STATUS_CANCELED);
         // Committed state is set back to the requested state once the override is cleared.
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), OTHER_DEVICE_STATE);
         assertFalse(mService.getOverrideState().isPresent());
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
@@ -297,7 +324,7 @@
                 TestDeviceStateManagerCallback.STATUS_ACTIVE);
         // Committed state changes as there is a requested override.
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
@@ -310,7 +337,7 @@
         // Committed state is set back to the requested state as the override state is no longer
         // supported.
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
         assertFalse(mService.getOverrideState().isPresent());
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
@@ -348,6 +375,10 @@
         });
     }
 
+    private static void assertArrayEquals(int[] expected, int[] actual) {
+        Assert.assertTrue(Arrays.equals(expected, actual));
+    }
+
     private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
         private final DeviceStateProvider mProvider;
         private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
@@ -429,12 +460,14 @@
         public static final int STATUS_SUSPENDED = 2;
         public static final int STATUS_CANCELED = 3;
 
-        private Integer mLastNotifiedValue;
+        @Nullable
+        private DeviceStateInfo mLastNotifiedInfo;
+        private boolean mNotifiedOfChangeInSupportedStates;
         private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>();
 
         @Override
-        public void onDeviceStateChanged(int deviceState) {
-            mLastNotifiedValue = deviceState;
+        public void onDeviceStateInfoChanged(DeviceStateInfo info) {
+            mLastNotifiedInfo = info;
         }
 
         @Override
@@ -453,8 +486,8 @@
         }
 
         @Nullable
-        Integer getLastNotifiedValue() {
-            return mLastNotifiedValue;
+        DeviceStateInfo getLastNotifiedInfo() {
+            return mLastNotifiedInfo;
         }
 
         int getLastNotifiedStatus(IBinder requestToken) {
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 990927b..d8e7582 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1965,17 +1965,17 @@
 
         // Non-resizable
         mAtm.mForceResizableActivities = false;
-        mAtm.mSizeCompatFreeform = false;
+        mAtm.mSupportsNonResizableMultiWindow = false;
         assertFalse(activity.supportsFreeform());
 
         // Force resizable
         mAtm.mForceResizableActivities = true;
-        mAtm.mSizeCompatFreeform = false;
+        mAtm.mSupportsNonResizableMultiWindow = false;
         assertTrue(activity.supportsFreeform());
 
         // Allow non-resizable
         mAtm.mForceResizableActivities = false;
-        mAtm.mSizeCompatFreeform = true;
+        mAtm.mSupportsNonResizableMultiWindow = true;
         assertTrue(activity.supportsFreeform());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index f8346efb..a1e5afb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -661,7 +661,7 @@
 
     @Test
     public void testForceMaximizesUnresizeableApp() {
-        mAtm.mSizeCompatFreeform = false;
+        mAtm.mSupportsNonResizableMultiWindow = false;
         final TestDisplayContent freeformDisplay = createNewDisplayContent(
                 WINDOWING_MODE_FREEFORM);
 
@@ -684,7 +684,7 @@
 
     @Test
     public void testLaunchesPortraitSizeCompatOnFreeformLandscapeDisplayWithFreeformSizeCompat() {
-        mAtm.mSizeCompatFreeform = true;
+        mAtm.mSupportsNonResizableMultiWindow = true;
         final TestDisplayContent freeformDisplay = createNewDisplayContent(
                 WINDOWING_MODE_FREEFORM);
 
@@ -712,7 +712,7 @@
 
     @Test
     public void testLaunchesLandscapeSizeCompatOnFreeformLandscapeDisplayWithFreeformSizeCompat() {
-        mAtm.mSizeCompatFreeform = true;
+        mAtm.mSupportsNonResizableMultiWindow = true;
         final TestDisplayContent freeformDisplay = createNewDisplayContent(
                 WINDOWING_MODE_FREEFORM);
         final ActivityOptions options = ActivityOptions.makeBasic();
@@ -728,7 +728,7 @@
 
     @Test
     public void testLaunchesPortraitUnresizableOnFreeformDisplayWithFreeformSizeCompat() {
-        mAtm.mSizeCompatFreeform = true;
+        mAtm.mSupportsNonResizableMultiWindow = true;
         final TestDisplayContent freeformDisplay = createNewDisplayContent(
                 WINDOWING_MODE_FREEFORM);
         final ActivityOptions options = ActivityOptions.makeBasic();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
index 0dd8d23..18c8cf5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
@@ -18,7 +18,6 @@
 
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
 import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH;
@@ -119,20 +118,6 @@
     }
 
     @Test
-    public void testEnableSizeCompatFreeform() {
-        try (BoolSettingsSession enableSizeCompatFreeformSession = new
-                BoolSettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) {
-            final boolean enableSizeCompatFreeform =
-                    !enableSizeCompatFreeformSession.getSetting();
-            final Uri enableSizeCompatFreeformUri =
-                    enableSizeCompatFreeformSession.setSetting(enableSizeCompatFreeform);
-            mWm.mSettingsObserver.onChange(false, enableSizeCompatFreeformUri);
-
-            assertEquals(mWm.mAtmService.mSizeCompatFreeform, enableSizeCompatFreeform);
-        }
-    }
-
-    @Test
     public void testSupportsNonResizableMultiWindow() {
         try (BoolSettingsSession supportsNonResizableMultiWindowSession = new
                 BoolSettingsSession(DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)) {