Merge "Update destination bounds if rotation finishes first" into rvc-dev
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 31d7d08..65e772cb 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -25,12 +25,14 @@
 import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 
@@ -572,6 +574,37 @@
     }
 
     /**
+     * Register this network agent with a testing harness.
+     *
+     * The returned Messenger sends messages to the Handler. This allows a test to send
+     * this object {@code CMD_*} messages as if they came from ConnectivityService, which
+     * is useful for testing the behavior.
+     *
+     * @hide
+     */
+    public Messenger registerForTest(final Network network) {
+        log("Registering NetworkAgent for test");
+        synchronized (mRegisterLock) {
+            mNetwork = network;
+            mInitialConfiguration = null;
+        }
+        return new Messenger(mHandler);
+    }
+
+    /**
+     * Waits for the handler to be idle.
+     * This is useful for testing, and has smaller scope than an accessor to mHandler.
+     * TODO : move the implementation in common library with the tests
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean waitForIdle(final long timeoutMs) {
+        final ConditionVariable cv = new ConditionVariable(false);
+        mHandler.post(cv::open);
+        return cv.block(timeoutMs);
+    }
+
+    /**
      * @return The Network associated with this agent, or null if it's not registered yet.
      */
     @Nullable
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 73c6b3d..52d6fdfb 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -680,11 +680,13 @@
     public void restrictCapabilitesForTestNetwork() {
         final long originalCapabilities = mNetworkCapabilities;
         final NetworkSpecifier originalSpecifier = mNetworkSpecifier;
+        final int originalSignalStrength = mSignalStrength;
         clearAll();
         // Reset the transports to only contain TRANSPORT_TEST.
         mTransportTypes = (1 << TRANSPORT_TEST);
         mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
         mNetworkSpecifier = originalSpecifier;
+        mSignalStrength = originalSignalStrength;
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
old mode 100644
new mode 100755
index 5418833..fe340c4
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4748,6 +4748,14 @@
         public static final String SHOW_BATTERY_PERCENT = "status_bar_show_battery_percent";
 
         /**
+         * Whether or not to enable multiple audio focus.
+         * When enabled, requires more management by user over application playback activity,
+         * for instance pausing media apps when another starts.
+         * @hide
+         */
+        public static final String MULTI_AUDIO_FOCUS_ENABLED = "multi_audio_focus_enabled";
+
+        /**
          * IMPORTANT: If you add a new public settings you also have to add it to
          * PUBLIC_SETTINGS below. If the new setting is hidden you have to add
          * it to PRIVATE_SETTINGS below. Also add a validator that can validate
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 1efa3dd..b47b2b4 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -31,6 +31,7 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
@@ -558,9 +559,10 @@
             }
         }
 
-        void reportResult(@Nullable List<Dataset> inlineSuggestionsData) {
+        void reportResult(@Nullable List<Dataset> inlineSuggestionsData,
+                @Nullable Bundle clientState) {
             try {
-                mCallback.onSuccess(inlineSuggestionsData);
+                mCallback.onSuccess(inlineSuggestionsData, clientState);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
             }
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 526a749..21738d80 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.os.Bundle;
 import android.service.autofill.Dataset;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.Log;
@@ -55,14 +56,15 @@
 
         if (response == null) {
             mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
-            mProxy.reportResult(/* inlineSuggestionsData */ null);
+            mProxy.reportResult(/* inlineSuggestionsData */ null, /* clientState */ null);
             return;
         }
 
         List<Dataset> inlineSuggestions = response.getInlineSuggestions();
+        Bundle clientState = response.getClientState();
         if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
             mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
-            mProxy.reportResult(inlineSuggestions);
+            mProxy.reportResult(inlineSuggestions, clientState);
             return;
         }
 
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index 24af1e5..609e382 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -30,7 +30,7 @@
  */
 interface IFillCallback {
     void onCancellable(in ICancellationSignal cancellation);
-    void onSuccess(in @nullable List<Dataset> inlineSuggestionsData);
+    void onSuccess(in @nullable List<Dataset> inlineSuggestionsData, in @nullable Bundle clientState);
     boolean isCompleted();
     void cancel();
 }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
old mode 100644
new mode 100755
index 8477aa3..3e1f72d
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6145,6 +6145,17 @@
         }
     }
 
+    /** @hide
+     * TODO: make this a @SystemApi */
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void setMultiAudioFocusEnabled(boolean enabled) {
+        try {
+            getService().setMultiAudioFocusEnabled(enabled);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     //---------------------------------------------------------
     // Inner classes
     //--------------------
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index d237975..ed566a5 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -31,6 +31,7 @@
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 
 import libcore.io.IoUtils;
@@ -586,7 +587,9 @@
     private static final int WEBP_CHUNK_SIZE_BYTE_LENGTH = 4;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @GuardedBy("sFormatter")
     private static SimpleDateFormat sFormatter;
+    @GuardedBy("sFormatterTz")
     private static SimpleDateFormat sFormatterTz;
 
     // See Exchangeable image file format for digital still cameras: Exif version 2.2.
@@ -2426,12 +2429,17 @@
         try {
             // The exif field is in local time. Parsing it as if it is UTC will yield time
             // since 1/1/1970 local time
-            Date datetime = sFormatter.parse(dateTimeString, pos);
+            Date datetime;
+            synchronized (sFormatter) {
+                datetime = sFormatter.parse(dateTimeString, pos);
+            }
 
             if (offsetString != null) {
                 dateTimeString = dateTimeString + " " + offsetString;
                 ParsePosition position = new ParsePosition(0);
-                datetime = sFormatterTz.parse(dateTimeString, position);
+                synchronized (sFormatterTz) {
+                    datetime = sFormatterTz.parse(dateTimeString, position);
+                }
             }
 
             if (datetime == null) return -1;
@@ -2473,7 +2481,10 @@
 
         ParsePosition pos = new ParsePosition(0);
         try {
-            Date datetime = sFormatter.parse(dateTimeString, pos);
+            final Date datetime;
+            synchronized (sFormatter) {
+                datetime = sFormatter.parse(dateTimeString, pos);
+            }
             if (datetime == null) return -1;
             return datetime.getTime();
         } catch (IllegalArgumentException e) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
old mode 100644
new mode 100755
index bb10e1f..e3b67f8
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -301,4 +301,6 @@
 
     // WARNING: read warning at top of file, new methods that need to be used by native
     // code via IAudioManager.h need to be added to the top section.
+
+    oneway void setMultiAudioFocusEnabled(in boolean enabled);
 }
diff --git a/media/java/android/media/midi/MidiDeviceInfo.java b/media/java/android/media/midi/MidiDeviceInfo.java
index c222985..dd3b6db 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.java
+++ b/media/java/android/media/midi/MidiDeviceInfo.java
@@ -19,7 +19,6 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-
 import android.util.Log;
 
 /**
@@ -205,6 +204,20 @@
     public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
             String[] inputPortNames, String[] outputPortNames, Bundle properties,
             boolean isPrivate) {
+        // Check num ports for out-of-range values. Typical values will be
+        // between zero and three. More than 16 would be very unlikely
+        // because the port index field in the USB packet is only 4 bits.
+        // This check is mainly just to prevent OutOfMemoryErrors when
+        // fuzz testing.
+        final int maxPorts = 256; // arbitrary and very high
+        if (numInputPorts < 0 || numInputPorts > maxPorts) {
+            throw new IllegalArgumentException("numInputPorts out of range = "
+                    + numInputPorts);
+        }
+        if (numOutputPorts < 0 || numOutputPorts > maxPorts) {
+            throw new IllegalArgumentException("numOutputPorts out of range = "
+                    + numOutputPorts);
+        }
         mType = type;
         mId = id;
         mInputPortCount = numInputPorts;
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
index 6b1f259..1d23c31 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
@@ -17,6 +17,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="enabled_by_admin" msgid="6630472777476410137">"ବ୍ୟବସ୍ଥାପକଙ୍କ ଦ୍ୱାରା ସକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="enabled_by_admin" msgid="6630472777476410137">"ଆଡମିନଙ୍କ ଦ୍ୱାରା ସକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"ବ୍ୟବସ୍ଥାପକଙ୍କ ଦ୍ଵାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 3024b84..a62d76f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -401,6 +401,11 @@
         }
 
         private List<MediaDevice> buildDisconnectedBluetoothDevice() {
+            if (mBluetoothAdapter == null) {
+                Log.w(TAG, "buildDisconnectedBluetoothDevice() BluetoothAdapter is null");
+                return new ArrayList<>();
+            }
+
             final List<BluetoothDevice> bluetoothDevices =
                     mBluetoothAdapter.getMostRecentlyConnectedDevices();
             final CachedBluetoothDeviceManager cachedDeviceManager =
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 7ddd64c..206c859 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -691,4 +691,28 @@
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(7);
         verify(mCallback).onDeviceListUpdate(any());
     }
+
+    @Test
+    public void onDeviceListAdded_bluetoothAdapterIsNull_noDisconnectedDeviceAdded() {
+        final List<MediaDevice> devices = new ArrayList<>();
+        final MediaDevice device1 = mock(MediaDevice.class);
+        final MediaDevice device2 = mock(MediaDevice.class);
+        final MediaDevice device3 = mock(MediaDevice.class);
+        mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
+        devices.add(device1);
+        devices.add(device2);
+        mLocalMediaManager.mMediaDevices.add(device3);
+        mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
+
+        mShadowBluetoothAdapter = null;
+
+        when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
+
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+        mLocalMediaManager.registerCallback(mCallback);
+        mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
+
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+        verify(mCallback).onDeviceListUpdate(any());
+    }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 01a2b69..03f6df0 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -101,7 +101,8 @@
                     Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
                     Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
                     Settings.System.SCREEN_BRIGHTNESS_FLOAT,
-                    Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT
+                    Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT,
+                    Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
                     );
 
     private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS =
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 5bf44c6..8051998 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -71,12 +71,13 @@
         public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
                 boolean homeTaskVisible, boolean clearedTask) {
             if (task.configuration.windowConfiguration.getWindowingMode()
-                    != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                    != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || !mDividerOptional.isPresent()) {
                 return;
             }
 
-            if (homeTaskVisible) {
-                showRecentApps(false /* triggeredFromAltTab */);
+            final Divider divider = mDividerOptional.get();
+            if (divider.isMinimized()) {
+                divider.onUndockingTask();
             }
         }
     };
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index 9328611..ab25747 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -44,7 +44,8 @@
         android:extractNativeLibs="false"
         android:persistent="true">
         <service android:name="com.android.server.connectivity.tethering.TetheringService"
-                 android:permission="android.permission.MAINLINE_NETWORK_STACK">
+                 android:permission="android.permission.MAINLINE_NETWORK_STACK"
+                 android:exported="true">
             <intent-filter>
                 <action android:name="android.net.ITetheringConnector"/>
             </intent-filter>
diff --git a/packages/Tethering/AndroidManifest_InProcess.xml b/packages/Tethering/AndroidManifest_InProcess.xml
index 02ea551..bf1f001 100644
--- a/packages/Tethering/AndroidManifest_InProcess.xml
+++ b/packages/Tethering/AndroidManifest_InProcess.xml
@@ -24,7 +24,8 @@
     <application>
         <service android:name="com.android.server.connectivity.tethering.TetheringService"
                  android:process="system"
-                 android:permission="android.permission.MAINLINE_NETWORK_STACK">
+                 android:permission="android.permission.MAINLINE_NETWORK_STACK"
+                 android:exported="true">
             <intent-filter>
                 <action android:name="android.net.ITetheringConnector.InProcess"/>
             </intent-filter>
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index d1805d9..1bc026c 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -815,26 +815,27 @@
         }
     }
 
-    void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId) {
+    void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
+            @Nullable Bundle clientState) {
         synchronized (mLock) {
             if (mAugmentedAutofillEventHistory == null
                     || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
                 return;
             }
             mAugmentedAutofillEventHistory.addEvent(
-                    new Event(Event.TYPE_DATASET_SELECTED, suggestionId, null, null, null,
+                    new Event(Event.TYPE_DATASET_SELECTED, suggestionId, clientState, null, null,
                             null, null, null, null, null, null));
         }
     }
 
-    void logAugmentedAutofillShown(int sessionId) {
+    void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState) {
         synchronized (mLock) {
             if (mAugmentedAutofillEventHistory == null
                     || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
                 return;
             }
             mAugmentedAutofillEventHistory.addEvent(
-                    new Event(Event.TYPE_DATASETS_SHOWN, null, null, null, null, null,
+                    new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
                             null, null, null, null, null));
 
         }
@@ -1185,15 +1186,16 @@
                         }
 
                         @Override
-                        public void logAugmentedAutofillShown(int sessionId) {
-                            AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId);
+                        public void logAugmentedAutofillShown(int sessionId, Bundle clientState) {
+                            AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId,
+                                    clientState);
                         }
 
                         @Override
                         public void logAugmentedAutofillSelected(int sessionId,
-                                String suggestionId) {
+                                String suggestionId, Bundle clientState) {
                             AutofillManagerServiceImpl.this.logAugmentedAutofillSelected(sessionId,
-                                    suggestionId);
+                                    suggestionId, clientState);
                         }
 
                         @Override
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index b6bc7c5..6cec8d8 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -168,12 +168,12 @@
                             focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
                             new IFillCallback.Stub() {
                                 @Override
-                                public void onSuccess(
-                                        @Nullable List<Dataset> inlineSuggestionsData) {
+                                public void onSuccess(@Nullable List<Dataset> inlineSuggestionsData,
+                                        @Nullable Bundle clientState) {
                                     mCallbacks.resetLastResponse();
                                     maybeRequestShowInlineSuggestions(sessionId,
                                             inlineSuggestionsRequest, inlineSuggestionsData,
-                                            focusedId, inlineSuggestionsCallback,
+                                            clientState, focusedId, inlineSuggestionsCallback,
                                             client, onErrorCallback, remoteRenderService);
                                     requestAutofill.complete(null);
                                 }
@@ -238,7 +238,8 @@
 
     private void maybeRequestShowInlineSuggestions(int sessionId,
             @Nullable InlineSuggestionsRequest request,
-            @Nullable List<Dataset> inlineSuggestionsData, @NonNull AutofillId focusedId,
+            @Nullable List<Dataset> inlineSuggestionsData, @Nullable Bundle clientState,
+            @NonNull AutofillId focusedId,
             @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback,
             @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
@@ -256,7 +257,7 @@
                             @Override
                             public void autofill(Dataset dataset) {
                                 mCallbacks.logAugmentedAutofillSelected(sessionId,
-                                        dataset.getId());
+                                        dataset.getId(), clientState);
                                 try {
                                     final ArrayList<AutofillId> fieldIds = dataset.getFieldIds();
                                     final int size = fieldIds.size();
@@ -287,7 +288,7 @@
             return;
         }
         if (inlineSuggestionsCallback.apply(inlineSuggestionsResponse)) {
-            mCallbacks.logAugmentedAutofillShown(sessionId);
+            mCallbacks.logAugmentedAutofillShown(sessionId, clientState);
         }
     }
 
@@ -310,8 +311,9 @@
 
         void setLastResponse(int sessionId);
 
-        void logAugmentedAutofillShown(int sessionId);
+        void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState);
 
-        void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId);
+        void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
+                @Nullable Bundle clientState);
     }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 51427c1..1a58d60 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2151,7 +2151,8 @@
     private boolean checkNetworkSignalStrengthWakeupPermission(int pid, int uid) {
         return checkAnyPermissionOf(pid, uid,
                 android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP,
-                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+                android.Manifest.permission.NETWORK_SETTINGS);
     }
 
     private void enforceConnectivityRestrictedNetworksPermission() {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b0a586d..14fe0c5 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3215,10 +3215,14 @@
 
     @Override
     public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
-        Slog.d(TAG, "unlockUserKey: " + userId);
+        boolean isFsEncrypted = StorageManager.isFileEncryptedNativeOrEmulated();
+        Slog.d(TAG, "unlockUserKey: " + userId
+                + " isFileEncryptedNativeOrEmulated: " + isFsEncrypted
+                + " hasToken: " + (token != null)
+                + " hasSecret: " + (secret != null));
         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
 
-        if (StorageManager.isFileEncryptedNativeOrEmulated()) {
+        if (isFsEncrypted) {
             try {
                 mVold.unlockUserKey(userId, serialNumber, encodeBytes(token),
                         encodeBytes(secret));
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
old mode 100644
new mode 100755
index 02d8571..ae22e69
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -8419,6 +8419,23 @@
     }
 
     //======================
+    // Multi Audio Focus
+    //======================
+    public void setMultiAudioFocusEnabled(boolean enabled) {
+        enforceModifyAudioRoutingPermission();
+        if (mMediaFocusControl != null) {
+            boolean mafEnabled = mMediaFocusControl.getMultiAudioFocusEnabled();
+            if (mafEnabled != enabled) {
+                mMediaFocusControl.updateMultiAudioFocus(enabled);
+                if (!enabled) {
+                    mDeviceBroker.postBroadcastBecomingNoisy();
+                }
+            }
+        }
+    }
+
+
+    //======================
     // misc
     //======================
     private final HashMap<IBinder, AudioPolicyProxy> mAudioPolicies =
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
old mode 100644
new mode 100755
index 7bf2bc8..bfab991
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -30,6 +30,7 @@
 import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.provider.Settings;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -80,6 +81,7 @@
     private final Context mContext;
     private final AppOpsManager mAppOps;
     private PlayerFocusEnforcer mFocusEnforcer; // never null
+    private boolean mMultiAudioFocusEnabled = false;
 
     private boolean mRingOrCallActive = false;
 
@@ -91,6 +93,8 @@
         mContext = cntxt;
         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
         mFocusEnforcer = pfe;
+        mMultiAudioFocusEnabled = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0) != 0;
     }
 
     protected void dump(PrintWriter pw) {
@@ -100,6 +104,7 @@
         pw.println("\n");
         // log
         mEventLogger.dump(pw);
+        dumpMultiAudioFocus(pw);
     }
 
     //=================================================================
@@ -194,6 +199,14 @@
                 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
             }
         }
+
+        if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
+            for (FocusRequester multifr : mMultiAudioFocusList) {
+                if (isLockedFocusOwner(multifr)) {
+                    multifr.handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
+                }
+            }
+        }
     }
 
     /**
@@ -203,17 +216,30 @@
      */
     @GuardedBy("mAudioFocusLock")
     private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr,
-            boolean forceDuck) {
+                                                   boolean forceDuck) {
         final List<String> clientsToRemove = new LinkedList<String>();
         // going through the audio focus stack to signal new focus, traversing order doesn't
         // matter as all entries respond to the same external focus gain
-        for (FocusRequester focusLoser : mFocusStack) {
-            final boolean isDefinitiveLoss =
-                    focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
-            if (isDefinitiveLoss) {
-                clientsToRemove.add(focusLoser.getClientId());
+        if (!mFocusStack.empty()) {
+            for (FocusRequester focusLoser : mFocusStack) {
+                final boolean isDefinitiveLoss =
+                        focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
+                if (isDefinitiveLoss) {
+                    clientsToRemove.add(focusLoser.getClientId());
+                }
             }
         }
+
+        if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
+            for (FocusRequester multifocusLoser : mMultiAudioFocusList) {
+                final boolean isDefinitiveLoss =
+                        multifocusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
+                if (isDefinitiveLoss) {
+                    clientsToRemove.add(multifocusLoser.getClientId());
+                }
+            }
+        }
+
         for (String clientToRemove : clientsToRemove) {
             removeFocusStackEntry(clientToRemove, false /*signal*/,
                     true /*notifyFocusFollowers*/);
@@ -222,6 +248,8 @@
 
     private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
 
+    ArrayList<FocusRequester> mMultiAudioFocusList = new ArrayList<FocusRequester>();
+
     /**
      * Helper function:
      * Display in the log the current entries in the audio focus stack
@@ -287,6 +315,22 @@
                 }
             }
         }
+
+        if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
+            Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator();
+            while (listIterator.hasNext()) {
+                FocusRequester fr = listIterator.next();
+                if (fr.hasSameClient(clientToRemove)) {
+                    listIterator.remove();
+                    fr.release();
+                }
+            }
+
+            if (signal) {
+                // notify the new top of the stack it gained focus
+                notifyTopOfAudioFocusStack();
+            }
+        }
     }
 
     /**
@@ -410,6 +454,16 @@
                     removeFocusEntryForExtPolicy(mCb);
                 } else {
                     removeFocusStackEntryOnDeath(mCb);
+                    if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
+                        Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator();
+                        while (listIterator.hasNext()) {
+                            FocusRequester fr = listIterator.next();
+                            if (fr.hasSameBinder(mCb)) {
+                                listIterator.remove();
+                                fr.release();
+                            }
+                        }
+                    }
                 }
             }
         }
@@ -867,6 +921,35 @@
 
             final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
                     clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk);
+
+            if (mMultiAudioFocusEnabled
+                    && (focusChangeHint == AudioManager.AUDIOFOCUS_GAIN)) {
+                if (enteringRingOrCall) {
+                    if (!mMultiAudioFocusList.isEmpty()) {
+                        for (FocusRequester multifr : mMultiAudioFocusList) {
+                            multifr.handleFocusLossFromGain(focusChangeHint, nfr, forceDuck);
+                        }
+                    }
+                } else {
+                    boolean needAdd = true;
+                    if (!mMultiAudioFocusList.isEmpty()) {
+                        for (FocusRequester multifr : mMultiAudioFocusList) {
+                            if (multifr.getClientUid() == Binder.getCallingUid()) {
+                                needAdd = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (needAdd) {
+                        mMultiAudioFocusList.add(nfr);
+                    }
+                    nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
+                    notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
+                            AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
+                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+                }
+            }
+
             if (focusGrantDelayed) {
                 // focusGrantDelayed being true implies we can't reassign focus right now
                 // which implies the focus stack is not empty.
@@ -877,9 +960,7 @@
                 return requestResult;
             } else {
                 // propagate the focus change through the stack
-                if (!mFocusStack.empty()) {
-                    propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck);
-                }
+                propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck);
 
                 // push focus requester at the top of the audio focus stack
                 mFocusStack.push(nfr);
@@ -970,4 +1051,39 @@
             }
         }.start();
     }
+
+    public void updateMultiAudioFocus(boolean enabled) {
+        Log.d(TAG, "updateMultiAudioFocus( " + enabled + " )");
+        mMultiAudioFocusEnabled = enabled;
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.MULTI_AUDIO_FOCUS_ENABLED, enabled ? 1 : 0);
+        if (!mFocusStack.isEmpty()) {
+            final FocusRequester fr = mFocusStack.peek();
+            fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false);
+        }
+        if (!enabled) {
+            if (!mMultiAudioFocusList.isEmpty()) {
+                for (FocusRequester multifr : mMultiAudioFocusList) {
+                    multifr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false);
+                }
+                mMultiAudioFocusList.clear();
+            }
+        }
+    }
+
+    public boolean getMultiAudioFocusEnabled() {
+        return mMultiAudioFocusEnabled;
+    }
+
+    private void dumpMultiAudioFocus(PrintWriter pw) {
+        pw.println("Multi Audio Focus enabled :" + mMultiAudioFocusEnabled);
+        if (!mMultiAudioFocusList.isEmpty()) {
+            pw.println("Multi Audio Focus List:");
+            pw.println("------------------------------");
+            for (FocusRequester multifr : mMultiAudioFocusList) {
+                multifr.dump(pw);
+            }
+            pw.println("------------------------------");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index c3413e8..0d89997 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -622,6 +622,16 @@
             return;
         }
 
+        if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission
+                && !TextUtils.equals(route.getId(),
+                routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId())) {
+            Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to"
+                    + route);
+            routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter(
+                    routerRecord, requestId);
+            return;
+        }
+
         long uniqueRequestId = toUniqueRequestId(routerRecord.mRouterId, requestId);
         routerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::requestCreateSessionOnHandler,
@@ -915,9 +925,6 @@
 
         RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
                 .findRouterforSessionLocked(uniqueSessionId);
-        if (routerRecord == null) {
-            return;
-        }
 
         long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
         managerRecord.mUserRecord.mHandler.sendMessage(
@@ -1272,15 +1279,6 @@
                         toOriginalRequestId(uniqueRequestId));
                 return;
             }
-            if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission
-                    && !TextUtils.equals(route.getId(),
-                            mSystemProvider.getDefaultRoute().getId())) {
-                Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to"
-                        + route);
-                notifySessionCreationFailedToRouter(routerRecord,
-                        toOriginalRequestId(uniqueRequestId));
-                return;
-            }
 
             SessionCreationRequest request =
                     new SessionCreationRequest(routerRecord, uniqueRequestId, route, managerRecord);
@@ -1404,11 +1402,11 @@
         }
 
         private void releaseSessionOnHandler(long uniqueRequestId,
-                @NonNull RouterRecord routerRecord, @NonNull String uniqueSessionId) {
+                @Nullable RouterRecord routerRecord, @NonNull String uniqueSessionId) {
             final RouterRecord matchingRecord = mSessionToRouterMap.get(uniqueSessionId);
             if (matchingRecord != routerRecord) {
-                Slog.w(TAG, "Ignoring releasing session from non-matching router."
-                        + " packageName=" + routerRecord.mPackageName
+                Slog.w(TAG, "Ignoring releasing session from non-matching router. packageName="
+                        + (routerRecord == null ? null : routerRecord.mPackageName)
                         + " uniqueSessionId=" + uniqueSessionId);
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 68224b5..75d6a09 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
@@ -48,6 +49,7 @@
 import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.Display;
+import android.view.InsetsState;
 import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
@@ -1203,6 +1205,17 @@
                         updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace,
                                 skipRemainingWindowsForTasks);
                         focusedWindowAdded |= windowState.isFocused();
+                    } else if (isUntouchableNavigationBar(windowState)) {
+                        // If this widow is navigation bar without touchable region, accounting the
+                        // region of navigation bar inset because all touch events from this region
+                        // would be received by launcher, i.e. this region is a un-touchable one
+                        // for the application.
+                        final InsetsState insetsState =
+                                dc.getInsetsStateController().getRawInsetsState();
+                        final Rect displayFrame =
+                                insetsState.getSource(ITYPE_NAVIGATION_BAR).getFrame();
+                        unaccountedSpace.op(displayFrame, unaccountedSpace,
+                                Region.Op.REVERSE_DIFFERENCE);
                     }
 
                     if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
@@ -1281,6 +1294,18 @@
             return false;
         }
 
+        private boolean isUntouchableNavigationBar(WindowState windowState) {
+            if (windowState.mAttrs.type != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) {
+                return false;
+            }
+
+            // Gets the touchable region.
+            Region touchableRegion = mTempRegion1;
+            windowState.getTouchableRegion(touchableRegion);
+
+            return touchableRegion.isEmpty();
+        }
+
         private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen,
                 Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
             if (windowState.mAttrs.type
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9c56788..e79b804 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2669,6 +2669,17 @@
             return this;
         }
 
+        // Ensure activity visibilities and update lockscreen occluded/dismiss state when
+        // finishing the top activity that occluded keyguard. So that, the
+        // ActivityStack#mTopActivityOccludesKeyguard can be updated and the activity below won't
+        // be resumed.
+        if (isState(PAUSED)
+                && mStackSupervisor.getKeyguardController().isKeyguardLocked()
+                && getStack().topActivityOccludesKeyguard()) {
+            getStack().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+                    false /* preserveWindows */, false /* notifyClients */);
+        }
+
         boolean activityRemoved = false;
 
         // If this activity is currently visible, and the resumed activity is not yet visible, then
@@ -5125,26 +5136,48 @@
     }
 
     void startFreezingScreen() {
+        startFreezingScreen(ROTATION_UNDEFINED /* overrideOriginalDisplayRotation */);
+    }
+
+    void startFreezingScreen(int overrideOriginalDisplayRotation) {
         ProtoLog.i(WM_DEBUG_ORIENTATION,
                 "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
                 appToken, isVisible(), mFreezingScreen, mVisibleRequested,
                 new RuntimeException().fillInStackTrace());
-        if (mVisibleRequested) {
-            if (!mFreezingScreen) {
-                mFreezingScreen = true;
-                mWmService.registerAppFreezeListener(this);
-                mWmService.mAppsFreezingScreen++;
-                if (mWmService.mAppsFreezingScreen == 1) {
-                    mWmService.startFreezingDisplayLocked(0, 0, getDisplayContent());
-                    mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
-                    mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
+        if (!mVisibleRequested) {
+            return;
+        }
+
+        // If the override is given, the rotation of display doesn't change but we still want to
+        // cover the activity whose configuration is changing by freezing the display and running
+        // the rotation animation.
+        final boolean forceRotation = overrideOriginalDisplayRotation != ROTATION_UNDEFINED;
+        if (!mFreezingScreen) {
+            mFreezingScreen = true;
+            mWmService.registerAppFreezeListener(this);
+            mWmService.mAppsFreezingScreen++;
+            if (mWmService.mAppsFreezingScreen == 1) {
+                if (forceRotation) {
+                    // Make sure normal rotation animation will be applied.
+                    mDisplayContent.getDisplayRotation().cancelSeamlessRotation();
                 }
+                mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */,
+                        mDisplayContent, overrideOriginalDisplayRotation);
+                mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+                mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
             }
-            final int count = mChildren.size();
-            for (int i = 0; i < count; i++) {
-                final WindowState w = mChildren.get(i);
-                w.onStartFreezingScreen();
-            }
+        }
+        if (forceRotation) {
+            // The rotation of the real display won't change, so in order to unfreeze the screen
+            // via {@link #checkAppWindowsReadyToShow}, the windows have to be able to call
+            // {@link WindowState#reportResized} (it is skipped if the window is freezing) to update
+            // the drawn state.
+            return;
+        }
+        final int count = mChildren.size();
+        for (int i = 0; i < count; i++) {
+            final WindowState w = mChildren.get(i);
+            w.onStartFreezingScreen();
         }
     }
 
@@ -6159,6 +6192,21 @@
         ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
     }
 
+    @Override
+    void onCancelFixedRotationTransform(int originalDisplayRotation) {
+        if (this != mDisplayContent.getLastOrientationSource()
+                || getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED) {
+            // Only need to handle the activity that should be rotated with display.
+            return;
+        }
+
+        // Perform rotation animation according to the rotation of this activity.
+        startFreezingScreen(originalDisplayRotation);
+        // This activity may relaunch or perform configuration change so once it has reported drawn,
+        // the screen can be unfrozen.
+        ensureActivityConfiguration(0 /* globalChanges */, !PRESERVE_WINDOWS);
+    }
+
     void setRequestedOrientation(int requestedOrientation) {
         setOrientation(requestedOrientation, mayFreezeScreenLocked());
         mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8d46a67..36d5583 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -495,6 +495,7 @@
      * The launching activity which is using fixed rotation transformation.
      *
      * @see #handleTopActivityLaunchingInDifferentOrientation
+     * @see DisplayRotation#shouldRotateSeamlessly
      */
     ActivityRecord mFixedRotationLaunchingApp;
 
@@ -1238,7 +1239,7 @@
 
         if (configChanged) {
             mWaitingForConfig = true;
-            mWmService.startFreezingDisplayLocked(0 /* exitAnim */, 0 /* enterAnim */, this);
+            mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
             sendNewConfiguration();
         }
 
@@ -1476,6 +1477,9 @@
             sendNewConfiguration();
             return true;
         }
+        // The display won't rotate (e.g. the orientation from sensor has updated again before
+        // applying rotation to display), so clear it to stop using seamless rotation.
+        mFixedRotationLaunchingApp = null;
         return false;
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index bef80f0..ebfe70c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -537,8 +537,29 @@
     }
 
     void prepareNormalRotationAnimation() {
+        cancelSeamlessRotation();
         final RotationAnimationPair anim = selectRotationAnimation();
-        mService.startFreezingDisplayLocked(anim.mExit, anim.mEnter, mDisplayContent);
+        mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent);
+    }
+
+    /**
+     * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
+     * set by previous {@link #updateRotationUnchecked}, but another orientation change happens
+     * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
+     * and it doesn't choose seamless rotation.
+     */
+    void cancelSeamlessRotation() {
+        if (!mRotatingSeamlessly) {
+            return;
+        }
+        mDisplayContent.forAllWindows(w -> {
+            if (w.mSeamlesslyRotated) {
+                w.finishSeamlessRotation(false /* timeout */);
+                w.mSeamlesslyRotated = false;
+            }
+        }, true /* traverseTopToBottom */);
+        mSeamlessRotationCount = 0;
+        mRotatingSeamlessly = false;
     }
 
     private void prepareSeamlessRotation() {
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index b92ead1..5f33ea1 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -41,7 +41,6 @@
 import android.os.Trace;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
-import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
@@ -117,8 +116,9 @@
     private BlackFrame mEnteringBlackFrame;
     private int mWidth, mHeight;
 
-    private int mOriginalRotation;
-    private int mOriginalWidth, mOriginalHeight;
+    private final int mOriginalRotation;
+    private final int mOriginalWidth;
+    private final int mOriginalHeight;
     private int mCurRotation;
 
     private Rect mOriginalDisplayRect = new Rect();
@@ -140,20 +140,18 @@
     /** Intensity of light/whiteness of the layout after rotation occurs. */
     private float mEndLuma;
 
-    public ScreenRotationAnimation(Context context, DisplayContent displayContent,
-            boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
-        mService = service;
-        mContext = context;
+    ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation) {
+        mService = displayContent.mWmService;
+        mContext = mService.mContext;
         mDisplayContent = displayContent;
         displayContent.getBounds(mOriginalDisplayRect);
 
         // Screenshot does NOT include rotation!
-        final Display display = displayContent.getDisplay();
-        int originalRotation = display.getRotation();
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final int realOriginalRotation = displayInfo.rotation;
         final int originalWidth;
         final int originalHeight;
-        DisplayInfo displayInfo = displayContent.getDisplayInfo();
-        if (fixedToUserRotation) {
+        if (displayContent.getDisplayRotation().isFixedToUserRotation()) {
             // Emulated orientation.
             mForceDefaultOrientation = true;
             originalWidth = displayContent.mBaseDisplayWidth;
@@ -163,8 +161,8 @@
             originalWidth = displayInfo.logicalWidth;
             originalHeight = displayInfo.logicalHeight;
         }
-        if (originalRotation == Surface.ROTATION_90
-                || originalRotation == Surface.ROTATION_270) {
+        if (realOriginalRotation == Surface.ROTATION_90
+                || realOriginalRotation == Surface.ROTATION_270) {
             mWidth = originalHeight;
             mHeight = originalWidth;
         } else {
@@ -173,10 +171,18 @@
         }
 
         mOriginalRotation = originalRotation;
-        mOriginalWidth = originalWidth;
-        mOriginalHeight = originalHeight;
+        // If the delta is not zero, the rotation of display may not change, but we still want to
+        // apply rotation animation because there should be a top app shown as rotated. So the
+        // specified original rotation customizes the direction of animation to have better look
+        // when restoring the rotated app to the same rotation as current display.
+        final int delta = DisplayContent.deltaRotation(originalRotation, realOriginalRotation);
+        final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270;
+        mOriginalWidth = flipped ? originalHeight : originalWidth;
+        mOriginalHeight = flipped ? originalWidth : originalHeight;
         mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();
 
+        // Check whether the current screen contains any secure content.
+        final boolean isSecure = displayContent.hasSecureWindowOnScreen();
         final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
         try {
             mBackColorSurface = displayContent.makeChildSurface(null)
@@ -202,7 +208,7 @@
             t2.apply(true /* sync */);
 
             // Capture a screenshot into the surface we just created.
-            final int displayId = display.getDisplayId();
+            final int displayId = displayContent.getDisplayId();
             final Surface surface = mService.mSurfaceFactory.get();
             surface.copyFrom(mScreenshotLayer);
             SurfaceControl.ScreenshotGraphicBuffer gb =
@@ -242,7 +248,7 @@
 
         ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
                     "  FREEZE %s: CREATE", mScreenshotLayer);
-        setRotation(t, originalRotation);
+        setRotation(t, realOriginalRotation);
         t.apply();
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9d87976..5c7d37b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -29,6 +29,7 @@
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.StatusBarManager.DISABLE_MASK;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_PC;
@@ -2941,7 +2942,7 @@
                 mClientFreezingScreen = true;
                 final long origId = Binder.clearCallingIdentity();
                 try {
-                    startFreezingDisplayLocked(exitAnim, enterAnim);
+                    startFreezingDisplay(exitAnim, enterAnim);
                     mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
                     mH.sendEmptyMessageDelayed(H.CLIENT_FREEZE_TIMEOUT, 5000);
                 } finally {
@@ -5479,13 +5480,17 @@
         return changed;
     }
 
-    void startFreezingDisplayLocked(int exitAnim, int enterAnim) {
-        startFreezingDisplayLocked(exitAnim, enterAnim,
-                getDefaultDisplayContentLocked());
+    void startFreezingDisplay(int exitAnim, int enterAnim) {
+        startFreezingDisplay(exitAnim, enterAnim, getDefaultDisplayContentLocked());
     }
 
-    void startFreezingDisplayLocked(int exitAnim, int enterAnim,
-            DisplayContent displayContent) {
+    void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent) {
+        startFreezingDisplay(exitAnim, enterAnim, displayContent,
+                ROTATION_UNDEFINED /* overrideOriginalRotation */);
+    }
+
+    void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,
+            int overrideOriginalRotation) {
         if (mDisplayFrozen || displayContent.getDisplayRotation().isRotatingSeamlessly()) {
             return;
         }
@@ -5529,14 +5534,12 @@
             screenRotationAnimation.kill();
         }
 
-        // Check whether the current screen contains any secure content.
-        boolean isSecure = displayContent.hasSecureWindowOnScreen();
-
         displayContent.updateDisplayInfo();
-        screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
-                displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
-                this);
-        displayContent.setRotationAnimation(screenRotationAnimation);
+        final int originalRotation = overrideOriginalRotation != ROTATION_UNDEFINED
+                ? overrideOriginalRotation
+                : displayContent.getDisplayInfo().rotation;
+        displayContent.setRotationAnimation(new ScreenRotationAnimation(displayContent,
+                originalRotation));
     }
 
     void stopFreezingDisplayLocked() {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e34b816..2d5c4c1 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -595,7 +595,17 @@
             // The window may be detached or detaching.
             return;
         }
+        final int originalRotation = getWindowConfiguration().getRotation();
         onConfigurationChanged(parent.getConfiguration());
+        onCancelFixedRotationTransform(originalRotation);
+    }
+
+    /**
+     * It is called when the window is using fixed rotation transform, and before display applies
+     * the same rotation, the rotation change for display is canceled, e.g. the orientation from
+     * sensor is updated to previous direction.
+     */
+    void onCancelFixedRotationTransform(int originalDisplayRotation) {
     }
 
     @Override
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 08f6409..bc66fa7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -32,6 +32,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
@@ -63,6 +64,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.never;
 
@@ -1024,6 +1026,38 @@
     }
 
     /**
+     * Verify that complete finish request for a show-when-locked activity must ensure the
+     * keyguard occluded state being updated.
+     */
+    @Test
+    public void testCompleteFinishing_showWhenLocked() {
+        // Make keyguard locked and set the top activity show-when-locked.
+        KeyguardController keyguardController = mActivity.mStackSupervisor.getKeyguardController();
+        doReturn(true).when(keyguardController).isKeyguardLocked();
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.mVisibleRequested = true;
+        topActivity.nowVisible = true;
+        topActivity.setState(RESUMED, "true");
+        doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
+                any() /* starting */, anyInt() /* configChanges */,
+                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
+        topActivity.setShowWhenLocked(true);
+
+        // Verify the stack-top activity is occluded keyguard.
+        assertEquals(topActivity, mStack.topRunningActivity());
+        assertTrue(mStack.topActivityOccludesKeyguard());
+
+        // Finish the top activity
+        topActivity.setState(PAUSED, "true");
+        topActivity.finishing = true;
+        topActivity.completeFinishing("test");
+
+        // Verify new top activity does not occlude keyguard.
+        assertEquals(mActivity, mStack.topRunningActivity());
+        assertFalse(mStack.topActivityOccludesKeyguard());
+    }
+
+    /**
      * Verify destroy activity request completes successfully.
      */
     @Test
@@ -1273,6 +1307,48 @@
     }
 
     @Test
+    public void testActivityOnCancelFixedRotationTransform() {
+        mService.mWindowManager.mIsFixedRotationTransformEnabled = true;
+        final DisplayRotation displayRotation = mActivity.mDisplayContent.getDisplayRotation();
+        spyOn(displayRotation);
+
+        final DisplayContent display = mActivity.mDisplayContent;
+        final int originalRotation = display.getRotation();
+
+        // Make {@link DisplayContent#sendNewConfiguration} not apply rotation immediately.
+        doReturn(true).when(displayRotation).isWaitingForRemoteRotation();
+        doReturn((originalRotation + 1) % 4).when(displayRotation).rotationForOrientation(
+                anyInt() /* orientation */, anyInt() /* lastRotation */);
+        // Set to visible so the activity can freeze the screen.
+        mActivity.setVisibility(true);
+
+        display.rotateInDifferentOrientationIfNeeded(mActivity);
+        display.mFixedRotationLaunchingApp = mActivity;
+        displayRotation.updateRotationUnchecked(false /* forceUpdate */);
+
+        assertTrue(displayRotation.isRotatingSeamlessly());
+
+        // Simulate the rotation has been updated to previous one, e.g. sensor updates before the
+        // remote rotation is completed.
+        doReturn(originalRotation).when(displayRotation).rotationForOrientation(
+                anyInt() /* orientation */, anyInt() /* lastRotation */);
+        display.updateOrientation();
+
+        final DisplayInfo rotatedInfo = mActivity.getFixedRotationTransformDisplayInfo();
+        mActivity.finishFixedRotationTransform();
+        final ScreenRotationAnimation rotationAnim = display.getRotationAnimation();
+        rotationAnim.setRotation(display.getPendingTransaction(), originalRotation);
+
+        // Because the display doesn't rotate, the rotated activity needs to cancel the fixed
+        // rotation. There should be a rotation animation to cover the change of activity.
+        verify(mActivity).onCancelFixedRotationTransform(rotatedInfo.rotation);
+        assertTrue(mActivity.isFreezingScreen());
+        assertFalse(displayRotation.isRotatingSeamlessly());
+        assertNotNull(rotationAnim);
+        assertTrue(rotationAnim.isRotating());
+    }
+
+    @Test
     public void testActivityOnDifferentDisplayUpdatesProcessOverride() {
         final ActivityRecord secondaryDisplayActivity =
                 createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
diff --git a/startop/iorap/src/com/google/android/startop/iorap/DexOptEvent.java b/startop/iorap/src/com/google/android/startop/iorap/DexOptEvent.java
new file mode 100644
index 0000000..72c5eaa
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/DexOptEvent.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Notifications for iorapd specifying when a package is updated by dexopt service.<br /><br />
+ *
+ * @hide
+ */
+public class DexOptEvent implements Parcelable {
+    public static final int TYPE_PACKAGE_UPDATE = 0;
+    private static final int TYPE_MAX = 0;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_PACKAGE_UPDATE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+    public final String packageName;
+
+    @NonNull
+    public static DexOptEvent createPackageUpdate(String packageName) {
+        return new DexOptEvent(TYPE_PACKAGE_UPDATE, packageName);
+    }
+
+    private DexOptEvent(@Type int type, String packageName) {
+        this.type = type;
+        this.packageName = packageName;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+        Objects.requireNonNull(packageName, "packageName");
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{DexOptEvent: packageName: %s}", packageName);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof DexOptEvent) {
+            return equals((DexOptEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(DexOptEvent other) {
+        return packageName.equals(other.packageName);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+        out.writeString(packageName);
+    }
+
+    private DexOptEvent(Parcel in) {
+        this.type = in.readInt();
+        this.packageName = in.readString();
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<DexOptEvent> CREATOR
+            = new Parcelable.Creator<DexOptEvent>() {
+        public DexOptEvent createFromParcel(Parcel in) {
+            return new DexOptEvent(in);
+        }
+
+        public DexOptEvent[] newArray(int size) {
+            return new DexOptEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index 0c25cfb..8f1d0ad 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -324,6 +324,11 @@
             String[] updated = updatedPackages.toArray(new String[0]);
             for (String packageName : updated) {
                 Log.d(TAG, "onPackagesUpdated: " + packageName);
+                invokeRemote(mIorapRemote,
+                    (IIorap remote) ->
+                        remote.onDexOptEvent(RequestId.nextValueForSequence(),
+                                DexOptEvent.createPackageUpdate(packageName))
+                );
             }
         }
     }
diff --git a/tests/net/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
similarity index 67%
rename from tests/net/java/android/net/CaptivePortalDataTest.kt
rename to tests/net/common/java/android/net/CaptivePortalDataTest.kt
index 0071438..bd1847b 100644
--- a/tests/net/java/android/net/CaptivePortalDataTest.kt
+++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
@@ -16,17 +16,22 @@
 
 package android.net
 
+import android.os.Build
 import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
 import com.android.testutils.assertParcelSane
 import com.android.testutils.assertParcelingIsLossless
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
 import kotlin.test.assertEquals
 import kotlin.test.assertNotEquals
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.Q)
 class CaptivePortalDataTest {
     private val data = CaptivePortalData.Builder()
             .setRefreshTime(123L)
@@ -63,6 +68,46 @@
         assertNotEqualsAfterChange { it.setCaptive(false) }
     }
 
+    @Test
+    fun testUserPortalUrl() {
+        assertEquals(Uri.parse("https://portal.example.com/test"), data.userPortalUrl)
+    }
+
+    @Test
+    fun testVenueInfoUrl() {
+        assertEquals(Uri.parse("https://venue.example.com/test"), data.venueInfoUrl)
+    }
+
+    @Test
+    fun testIsSessionExtendable() {
+        assertTrue(data.isSessionExtendable)
+    }
+
+    @Test
+    fun testByteLimit() {
+        assertEquals(456L, data.byteLimit)
+        // Test byteLimit unset.
+        assertEquals(-1L, CaptivePortalData.Builder(null).build().byteLimit)
+    }
+
+    @Test
+    fun testRefreshTimeMillis() {
+        assertEquals(123L, data.refreshTimeMillis)
+    }
+
+    @Test
+    fun testExpiryTimeMillis() {
+        assertEquals(789L, data.expiryTimeMillis)
+        // Test expiryTimeMillis unset.
+        assertEquals(-1L, CaptivePortalData.Builder(null).build().expiryTimeMillis)
+    }
+
+    @Test
+    fun testIsCaptive() {
+        assertTrue(data.isCaptive)
+        assertFalse(makeBuilder().setCaptive(false).build().isCaptive)
+    }
+
     private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
             CaptivePortalData.Builder(this).apply { mutator(this) }.build()