Merge "[framework] Add getRoutingTable Oem extension API." into main
diff --git a/api/Android.bp b/api/Android.bp
index 3c92cb2..7ef00b0 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -73,6 +73,7 @@
         "framework-bluetooth",
         "framework-configinfrastructure",
         "framework-connectivity",
+        "framework-connectivity-b",
         "framework-connectivity-t",
         "framework-devicelock",
         "framework-graphics",
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index a413bbd..16f0693 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -1,13 +1,16 @@
 android.content.AsyncTaskLoader$LoadTask
+android.media.MediaCodecInfo$CodecCapabilities$FeatureList
 android.net.ConnectivityThread$Singleton
 android.os.FileObserver
 android.os.NullVibrator
+android.permission.PermissionManager
+android.provider.MediaStore
 android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
+android.view.HdrRenderState
 android.widget.Magnifier
+com.android.internal.jank.InteractionJankMonitor$InstanceHolder
+com.android.internal.util.LatencyTracker$SLatencyTrackerHolder
 gov.nist.core.net.DefaultNetworkLayer
-android.net.rtp.AudioGroup
-android.net.rtp.AudioStream
-android.net.rtp.RtpStream
 java.util.concurrent.ThreadLocalRandom
 java.util.ImmutableCollections
-com.android.internal.jank.InteractionJankMonitor$InstanceHolder
+sun.nio.fs.UnixChannelFactory
diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig
index 1b8471d..c2cc9d1 100644
--- a/core/java/android/database/sqlite/flags.aconfig
+++ b/core/java/android/database/sqlite/flags.aconfig
@@ -2,13 +2,6 @@
 container: "system"
 
 flag {
-     name: "oneway_finalizer_close"
-     namespace: "system_performance"
-     description: "Make BuildCursorNative.close oneway if in the the finalizer"
-     bug: "368221351"
-}
-
-flag {
      name: "oneway_finalizer_close_fixed"
      namespace: "system_performance"
      is_fixed_read_only: true
diff --git a/core/java/android/hardware/location/OWNERS b/core/java/android/hardware/location/OWNERS
index 747f909..340d6f2 100644
--- a/core/java/android/hardware/location/OWNERS
+++ b/core/java/android/hardware/location/OWNERS
@@ -9,4 +9,4 @@
 yuhany@google.com
 
 # ContextHub team
-per-file *ContextHub*,*NanoApp* = file:platform/system/chre:/OWNERS
+per-file Android.bp,*Hub*,*NanoApp* = file:platform/system/chre:/OWNERS
diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS
index 0809de2..ce79f5d 100644
--- a/core/java/android/print/OWNERS
+++ b/core/java/android/print/OWNERS
@@ -2,3 +2,4 @@
 
 anothermark@google.com
 kumarashishg@google.com
+bmgordon@google.com
diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS
index 0809de2..ce79f5d 100644
--- a/core/java/android/printservice/OWNERS
+++ b/core/java/android/printservice/OWNERS
@@ -2,3 +2,4 @@
 
 anothermark@google.com
 kumarashishg@google.com
+bmgordon@google.com
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 8358b9a..1dd9d46 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -75,8 +75,7 @@
 @android.ravenwood.annotation.RavenwoodClassLoadHook(
         "com.android.platform.test.ravenwood.runtimehelper.ClassLoadHook.onClassLoaded")
 // Uncomment the following annotation to switch to the Java substitution version.
-//@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
-//        "com.android.platform.test.ravenwood.nativesubstitution.Log_host")
+@android.ravenwood.annotation.RavenwoodRedirectionClass("Log_host")
 public final class Log {
     /** @hide */
     @IntDef({ASSERT, ERROR, WARN, INFO, DEBUG, VERBOSE})
@@ -250,6 +249,7 @@
      *         tag limit of concern after this API level.
      */
     @FastNative
+    @android.ravenwood.annotation.RavenwoodRedirect
     public static native boolean isLoggable(@Nullable String tag, @Level int level);
 
     /**
@@ -425,6 +425,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @android.ravenwood.annotation.RavenwoodRedirect
     public static native int println_native(int bufID, int priority, String tag, String msg);
 
     /**
@@ -452,6 +453,7 @@
      * Return the maximum payload the log daemon accepts without truncation.
      * @return LOGGER_ENTRY_MAX_PAYLOAD.
      */
+    @android.ravenwood.annotation.RavenwoodRedirect
     private static native int logger_entry_max_payload_native();
 
     /**
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 5606467..675c8f8 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -92,6 +92,7 @@
     method public void onDisable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
     method public void onDisableFinished(int);
     method public void onDisableStarted();
+    method public void onEeListenActivated(boolean);
     method public void onEnable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
     method public void onEnableFinished(int);
     method public void onEnableStarted();
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index 7d0837a..7f1fd15 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -46,6 +46,7 @@
    void onCardEmulationActivated(boolean isActivated);
    void onRfFieldActivated(boolean isActivated);
    void onRfDiscoveryStarted(boolean isDiscoveryStarted);
+   void onEeListenActivated(boolean isActivated);
    void onGetOemAppSearchIntent(in List<String> firstPackage, in ResultReceiver intentConsumer);
    void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent);
    void onLaunchHceAppChooserActivity(in String selectedAid, in List<ApduServiceInfo> services, in ComponentName failedComponent, in String category);
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 604a0f3..1bfe714 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -86,6 +86,7 @@
     private boolean mCardEmulationActivated = false;
     private boolean mRfFieldActivated = false;
     private boolean mRfDiscoveryStarted = false;
+    private boolean mEeListenActivated = false;
 
     /**
      * Broadcast Action: Sent on NFC stack initialization when NFC OEM extensions are enabled.
@@ -332,6 +333,13 @@
         void onRfDiscoveryStarted(boolean isDiscoveryStarted);
 
         /**
+        * Notifies the NFCEE (NFC Execution Environment) Listen has been activated.
+        *
+        * @param isActivated true, if EE Listen is ON, else EE Listen is OFF.
+        */
+        void onEeListenActivated(boolean isActivated);
+
+        /**
          * Gets the intent to find the OEM package in the OEM App market. If the consumer returns
          * {@code null} or a timeout occurs, the intent from the first available package will be
          * used instead.
@@ -442,6 +450,7 @@
                 callback.onCardEmulationActivated(mCardEmulationActivated);
                 callback.onRfFieldActivated(mRfFieldActivated);
                 callback.onRfDiscoveryStarted(mRfDiscoveryStarted);
+                callback.onEeListenActivated(mEeListenActivated);
             });
         }
     }
@@ -749,6 +758,13 @@
         }
 
         @Override
+        public void onEeListenActivated(boolean isActivated) throws RemoteException {
+            mEeListenActivated = isActivated;
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(isActivated, cb::onEeListenActivated, ex));
+        }
+
+        @Override
         public void onStateUpdated(int state) throws RemoteException {
             mCallbackMap.forEach((cb, ex) ->
                     handleVoidCallback(state, cb::onStateUpdated, ex));
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index 5727f99..3d293f7 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -52,10 +52,12 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.TreeMap;
 import java.util.regex.Pattern;
 
 /**
@@ -181,7 +183,8 @@
         this(info, onHost, description, staticAidGroups, dynamicAidGroups,
                 requiresUnlock, requiresScreenOn, bannerResource, uid,
                 settingsActivityName, offHost, staticOffHost, isEnabled,
-                new HashMap<String, Boolean>(), new HashMap<Pattern, Boolean>());
+                new HashMap<String, Boolean>(), new TreeMap<>(
+                        Comparator.comparing(Pattern::toString)));
     }
 
     /**
@@ -317,7 +320,8 @@
             mStaticAidGroups = new HashMap<String, AidGroup>();
             mDynamicAidGroups = new HashMap<String, AidGroup>();
             mAutoTransact = new HashMap<String, Boolean>();
-            mAutoTransactPatterns = new HashMap<Pattern, Boolean>();
+            mAutoTransactPatterns = new TreeMap<Pattern, Boolean>(
+                    Comparator.comparing(Pattern::toString));
             mOnHost = onHost;
 
             final int depth = parser.getDepth();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS
new file mode 100644
index 0000000..a2001e6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/accessibility/OWNERS
\ No newline at end of file
diff --git a/packages/Vcn/framework-b/Android.bp b/packages/Vcn/framework-b/Android.bp
new file mode 100644
index 0000000..be64bb1
--- /dev/null
+++ b/packages/Vcn/framework-b/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_team: "trendy_team_enigma",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_defaults {
+    name: "framework-connectivity-b-defaults",
+    sdk_version: "module_current",
+    min_sdk_version: "35", // TODO: Make it Android 25Q2 when this is included in mainline
+    defaults: ["framework-module-defaults"], // This is a boot jar
+
+    srcs: [
+        "src/**/*.java",
+    ],
+}
+
+java_sdk_library {
+    name: "framework-connectivity-b",
+    defaults: [
+        "framework-connectivity-b-defaults",
+    ],
+
+    permitted_packages: [
+        "android.net.vcn",
+    ],
+
+    // TODO: b/375213246 Expose this library to Tethering module
+}
diff --git a/packages/Vcn/framework-b/api/current.txt b/packages/Vcn/framework-b/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Vcn/framework-b/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/module-lib-current.txt b/packages/Vcn/framework-b/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Vcn/framework-b/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/module-lib-removed.txt b/packages/Vcn/framework-b/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Vcn/framework-b/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/removed.txt b/packages/Vcn/framework-b/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Vcn/framework-b/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/system-current.txt b/packages/Vcn/framework-b/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Vcn/framework-b/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/system-removed.txt b/packages/Vcn/framework-b/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Vcn/framework-b/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java b/packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java
new file mode 100644
index 0000000..fb5e153
--- /dev/null
+++ b/packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open 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.net.vcn;
+
+/**
+ * Placeholder class so new framework-vcn isn't empty
+ *
+ * @hide
+ */
+// This class will be removed once source code is migrated
+public class Placeholder {}
diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp
new file mode 100644
index 0000000..a462297
--- /dev/null
+++ b/packages/Vcn/service-b/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_team: "trendy_team_enigma",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+    name: "service-connectivity-b-pre-jarjar",
+    sdk_version: "system_server_current",
+    min_sdk_version: "35", // TODO: Make it Android 25Q2 when this is included in mainline
+    defaults: ["framework-system-server-module-defaults"], // This is a system server jar
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    // TODO: b/375213246 Expose this library to Tethering module
+    visibility: [
+        "//frameworks/base/services",
+    ],
+}
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java b/packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java
new file mode 100644
index 0000000..e799145
--- /dev/null
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+/**
+ * Placeholder class so new service-vcn isn't empty
+ *
+ * @hide
+ */
+// This class will be removed once source code is migrated
+public class Placeholder {}
diff --git a/packages/overlays/Android.bp b/packages/overlays/Android.bp
index 5075f63..44abb9f 100644
--- a/packages/overlays/Android.bp
+++ b/packages/overlays/Android.bp
@@ -21,6 +21,7 @@
 
 phony {
     name: "frameworks-base-overlays",
+    product_specific: true,
     required: [
         "DisplayCutoutEmulationCornerOverlay",
         "DisplayCutoutEmulationDoubleOverlay",
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index b3f78ab..4731cfb 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -279,6 +279,15 @@
     shared_libs: [
         "liblog",
     ],
+    visibility: ["//visibility:private"],
+}
+
+cc_library_host_shared {
+    name: "libravenwood_initializer",
+    defaults: ["ravenwood_jni_defaults"],
+    srcs: [
+        "runtime-jni/ravenwood_initializer.cpp",
+    ],
 }
 
 // We need this as a separate library because we need to overload the
@@ -301,7 +310,6 @@
         "libutils",
         "libcutils",
     ],
-    visibility: ["//frameworks/base"],
 }
 
 // For collecting the *stats.csv files in a known directory under out/host/linux-x86/testcases/.
@@ -659,6 +667,7 @@
     ],
     jni_libs: [
         // Libraries has to be loaded in the following order
+        "libravenwood_initializer",
         "libravenwood_sysprop",
         "libravenwood_runtime",
         "libandroid_runtime",
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
index d29b93c..a208d6d 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
@@ -40,7 +40,7 @@
      * See frameworks/base/core/jni/platform/host/HostRuntime.cpp
      */
     private static final Class<?>[] sLibandroidClasses = {
-            android.util.Log.class,
+//            android.util.Log.class, // Not using native log: b/377377826
             android.os.Parcel.class,
             android.os.Binder.class,
             android.os.SystemProperties.class,
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 28c262d..9177857 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -51,6 +51,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.hoststubgen.hosthelper.HostTestUtils;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.RuntimeInit;
 import com.android.ravenwood.RavenwoodRuntimeNative;
 import com.android.ravenwood.RavenwoodRuntimeState;
@@ -86,6 +87,7 @@
     }
 
     private static final String MAIN_THREAD_NAME = "RavenwoodMain";
+    private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer";
     private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop";
     private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
     private static final String RAVENWOOD_BUILD_PROP =
@@ -139,23 +141,61 @@
         return res;
     }
 
+    private static final Object sInitializationLock = new Object();
+
+    @GuardedBy("sInitializationLock")
+    private static boolean sInitialized = false;
+
+    @GuardedBy("sInitializationLock")
+    private static Throwable sExceptionFromGlobalInit;
+
     private static RavenwoodAwareTestRunner sRunner;
     private static RavenwoodSystemProperties sProps;
-    private static boolean sInitialized = false;
 
     /**
      * Initialize the global environment.
      */
     public static void globalInitOnce() {
-        if (sInitialized) {
-            return;
+        synchronized (sInitializationLock) {
+            if (!sInitialized) {
+                // globalInitOnce() is called from class initializer, which cause
+                // this method to be called recursively,
+                sInitialized = true;
+
+                // This is the first call.
+                try {
+                    globalInitInner();
+                } catch (Throwable th) {
+                    Log.e(TAG, "globalInit() failed", th);
+
+                    sExceptionFromGlobalInit = th;
+                    throw th;
+                }
+            } else {
+                // Subsequent calls. If the first call threw, just throw the same error, to prevent
+                // the test from running.
+                if (sExceptionFromGlobalInit != null) {
+                    Log.e(TAG, "globalInit() failed re-throwing the same exception",
+                            sExceptionFromGlobalInit);
+
+                    SneakyThrow.sneakyThrow(sExceptionFromGlobalInit);
+                }
+            }
         }
-        sInitialized = true;
+    }
+
+    private static void globalInitInner() {
+        if (RAVENWOOD_VERBOSE_LOGGING) {
+            Log.v(TAG, "globalInit() called here...", new RuntimeException("NOT A CRASH"));
+        }
+
+        // Some process-wide initialization. (maybe redirect stdout/stderr)
+        RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME);
 
         // We haven't initialized liblog yet, so directly write to System.out here.
-        RavenwoodCommonUtils.log(TAG, "globalInit()");
+        RavenwoodCommonUtils.log(TAG, "globalInitInner()");
 
-        // Load libravenwood_sysprop first
+        // Load libravenwood_sysprop before other libraries that may use SystemProperties.
         var libProp = RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_SYSPROP_NAME);
         System.load(libProp);
         RavenwoodRuntimeNative.reloadNativeLibrary(libProp);
diff --git a/ravenwood/runtime-jni/ravenwood_initializer.cpp b/ravenwood/runtime-jni/ravenwood_initializer.cpp
new file mode 100644
index 0000000..89fb7c3
--- /dev/null
+++ b/ravenwood/runtime-jni/ravenwood_initializer.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open 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.
+ */
+
+ /*
+  * This file is compiled into a single SO file, which we load at the very first.
+  * We can do process-wide initialization here.
+  */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "jni_helper.h"
+
+static void maybeRedirectLog() {
+    auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
+    if (ravenwoodLogOut == NULL) {
+        return;
+    }
+    ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut);
+
+    // Redirect stdin / stdout to /dev/tty.
+    int ttyFd = open(ravenwoodLogOut, O_WRONLY | O_APPEND);
+    if (ttyFd == -1) {
+        ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut,
+                strerror(errno));
+        return;
+    }
+    dup2(ttyFd, 1);
+    dup2(ttyFd, 2);
+}
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+    ALOGI("%s: JNI_OnLoad", __FILE__);
+
+    maybeRedirectLog();
+    return JNI_VERSION_1_4;
+}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index 5b75e98..c1993f6 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -180,24 +180,6 @@
     return syscall(__NR_gettid);
 }
 
-static void maybeRedirectLog() {
-    auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
-    if (ravenwoodLogOut == NULL) {
-        return;
-    }
-    ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut);
-
-    // Redirect stdin / stdout to /dev/tty.
-    int ttyFd = open(ravenwoodLogOut, O_WRONLY);
-    if (ttyFd == -1) {
-        ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut,
-                strerror(errno));
-        return;
-    }
-    dup2(ttyFd, 1);
-    dup2(ttyFd, 2);
-}
-
 // ---- Registration ----
 
 extern void register_android_system_OsConstants(JNIEnv* env);
@@ -218,8 +200,6 @@
 };
 
 extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
-    maybeRedirectLog();
-
     ALOGI("%s: JNI_OnLoad", __FILE__);
 
     JNIEnv* env = GetJNIEnvOrDie(vm);
diff --git a/ravenwood/scripts/run-ravenwood-tests.sh b/ravenwood/scripts/run-ravenwood-tests.sh
index 672c685..1910100 100755
--- a/ravenwood/scripts/run-ravenwood-tests.sh
+++ b/ravenwood/scripts/run-ravenwood-tests.sh
@@ -26,7 +26,7 @@
 
 
 # Regex to identify slow tests, in PCRE
-SLOW_TEST_RE='^(SystemUiRavenTests|CtsIcuTestCasesRavenwood)$'
+SLOW_TEST_RE='^(SystemUiRavenTests|CtsIcuTestCasesRavenwood|CarSystemUIRavenTests)$'
 
 smoke=0
 include_re=""
@@ -67,7 +67,7 @@
     if [[ "$re" == "" ]] ; then
         cat # No filtering
     else
-        grep $grep_arg -P "$re"
+        grep $grep_arg -iP "$re"
     fi
 }
 
diff --git a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
index 8ce15f0..52bf92e 100644
--- a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
+++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
@@ -19,18 +19,23 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 
 import android.content.Context;
 import android.hardware.SerialManager;
 import android.hardware.SerialManagerInternal;
 import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodRule;
+import android.platform.test.ravenwood.RavenwoodConfig;
+import android.platform.test.ravenwood.RavenwoodConfig.Config;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.server.LocalServices;
 
-import org.junit.Rule;
+import com.google.common.collect.Lists;
+
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -38,18 +43,25 @@
 public class RavenwoodServicesTest {
     private static final String TEST_VIRTUAL_PORT = "virtual:example";
 
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+    @Config
+    public static final RavenwoodConfig sRavenwood = new RavenwoodConfig.Builder()
             .setProcessSystem()
             .setServicesRequired(SerialManager.class)
             .build();
 
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    }
+
     @Test
     public void testDefined() {
         final SerialManager fromName = (SerialManager)
-                mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
+                mContext.getSystemService(Context.SERIAL_SERVICE);
         final SerialManager fromClass =
-                mRavenwood.getContext().getSystemService(SerialManager.class);
+                mContext.getSystemService(SerialManager.class);
         assertNotNull(fromName);
         assertNotNull(fromClass);
         assertEquals(fromName, fromClass);
@@ -63,9 +75,9 @@
         // Verify that we can obtain a manager, and talk to the backend service, and that no
         // serial ports are configured by default
         final SerialManager service = (SerialManager)
-                mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
+                mContext.getSystemService(Context.SERIAL_SERVICE);
         final String[] ports = service.getSerialPorts();
-        final String[] refPorts = mRavenwood.getContext().getResources().getStringArray(
+        final String[] refPorts = mContext.getResources().getStringArray(
                 com.android.internal.R.array.config_serialPorts);
         assertArrayEquals(refPorts, ports);
     }
@@ -73,7 +85,7 @@
     @Test
     public void testDriven() {
         final SerialManager service = (SerialManager)
-                mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
+                mContext.getSystemService(Context.SERIAL_SERVICE);
         final SerialManagerInternal internal = LocalServices.getService(
                 SerialManagerInternal.class);
 
@@ -81,8 +93,17 @@
             throw new UnsupportedOperationException(
                     "Needs socketpair() to offer accurate emulation");
         });
-        final String[] ports = service.getSerialPorts();
-        assertEquals(1, ports.length);
-        assertEquals(TEST_VIRTUAL_PORT, ports[0]);
+        try {
+            final String[] ports = service.getSerialPorts();
+            for (var port : ports) {
+                if (TEST_VIRTUAL_PORT.equals(port)) {
+                    return; // Pass
+                }
+            }
+            fail("Virtual port " + TEST_VIRTUAL_PORT + " not found. Actual="
+                    + Lists.newArrayList(ports));
+        } finally {
+            internal.removeVirtualSerialPortForTest(TEST_VIRTUAL_PORT);
+        }
     }
 }
diff --git a/services/Android.bp b/services/Android.bp
index e8d6630..d99ed3d 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -242,6 +242,7 @@
         "services.wifi",
         "service-blobstore",
         "service-jobscheduler",
+        "service-connectivity-b-pre-jarjar", // Move it to mainline module
         "android.hidl.base-V1.0-java",
     ],
 
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 2e9a4dc..a10039f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -462,7 +462,9 @@
 
                         @Override
                         public void onShown() {
-                            mCallback.onShown(UI_TYPE_DIALOG, response.getDatasets().size());
+                            if (mCallback != null) {
+                                mCallback.onShown(UI_TYPE_DIALOG, response.getDatasets().size());
+                            }
                         }
 
                         @Override
@@ -511,7 +513,9 @@
 
                         @Override
                         public void startIntentSender(IntentSender intentSender) {
-                            mCallback.startIntentSenderAndFinishSession(intentSender);
+                            if (mCallback != null) {
+                                mCallback.startIntentSenderAndFinishSession(intentSender);
+                            }
                         }
 
                         private void log(int type) {
diff --git a/services/core/java/com/android/server/SerialService.java b/services/core/java/com/android/server/SerialService.java
index 82c2038..71885fd 100644
--- a/services/core/java/com/android/server/SerialService.java
+++ b/services/core/java/com/android/server/SerialService.java
@@ -18,22 +18,30 @@
 
 import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.ISerialManager;
 import android.hardware.SerialManagerInternal;
 import android.os.ParcelFileDescriptor;
 import android.os.PermissionEnforcer;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.function.Supplier;
 
 @android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class SerialService extends ISerialManager.Stub {
+    private static final String TAG = "SerialService";
+
     private final Context mContext;
 
     @GuardedBy("mSerialPorts")
@@ -50,7 +58,7 @@
             final String[] serialPorts = getSerialPorts(context);
             for (String serialPort : serialPorts) {
                 mSerialPorts.put(serialPort, () -> {
-                    return native_open(serialPort);
+                    return tryOpen(serialPort);
                 });
             }
         }
@@ -135,5 +143,14 @@
         }
     };
 
-    private native ParcelFileDescriptor native_open(String path);
+    private static @Nullable ParcelFileDescriptor tryOpen(String path) {
+        try {
+            FileDescriptor fd = Os.open(path, OsConstants.O_RDWR | OsConstants.O_NOCTTY, 0);
+            return new ParcelFileDescriptor(fd);
+        } catch (ErrnoException e) {
+            Slog.e(TAG, "Could not open: " + path, e);
+            // We return null to preserve API semantics from earlier implementation variants.
+            return null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 2a30ad0..6f99673 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -61,6 +61,9 @@
 per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
 per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
 
+# Activity Security
+per-file ActivityManager* = file:/ACTIVITY_SECURITY_OWNERS
+
 # Londoners
 michaelwr@google.com #{LAST_RESORT_SUGGESTION}
 narayan@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 97c32b9..4993412 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -255,17 +255,6 @@
         }
     }
 
-    private static boolean isCompatibleLocale(Locale systemLocale, Locale keyboardLocale) {
-        // Different languages are never compatible
-        if (!systemLocale.getLanguage().equals(keyboardLocale.getLanguage())) {
-            return false;
-        }
-        // If both the system and the keyboard layout have a country specifier, they must be equal.
-        return TextUtils.isEmpty(systemLocale.getCountry())
-                || TextUtils.isEmpty(keyboardLocale.getCountry())
-                || systemLocale.getCountry().equals(keyboardLocale.getCountry());
-    }
-
     @MainThread
     private void updateKeyboardLayouts() {
         // Scan all input devices state for keyboard layouts that have been uninstalled.
@@ -953,21 +942,33 @@
             return;
         }
 
+        List<String> layoutNames = new ArrayList<>();
+        for (String layoutDesc : config.getConfiguredLayouts()) {
+            KeyboardLayout kl = getKeyboardLayout(layoutDesc);
+            if (kl == null) {
+                // b/349033234: Weird state with stale keyboard layout configured.
+                // Possibly due to race condition between KCM providing package being removed and
+                // corresponding layouts being removed from Datastore and cache.
+                // {@see updateKeyboardLayouts()}
+                //
+                // Ideally notification will be correctly shown after the keyboard layouts are
+                // configured again with the new package state.
+                return;
+            }
+            layoutNames.add(kl.getLabel());
+        }
         showKeyboardLayoutNotification(
                 r.getString(
                         R.string.keyboard_layout_notification_selected_title,
                         inputDevice.getName()),
-                createConfiguredNotificationText(mContext, config.getConfiguredLayouts()),
+                createConfiguredNotificationText(mContext, layoutNames),
                 inputDevice);
     }
 
     @MainThread
     private String createConfiguredNotificationText(@NonNull Context context,
-            @NonNull Set<String> selectedLayouts) {
+            @NonNull List<String> layoutNames) {
         final Resources r = context.getResources();
-        List<String> layoutNames = new ArrayList<>();
-        selectedLayouts.forEach(
-                (layoutDesc) -> layoutNames.add(getKeyboardLayout(layoutDesc).getLabel()));
         Collections.sort(layoutNames);
         switch (layoutNames.size()) {
             case 1:
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 89555a9..c8a8799 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -161,6 +161,11 @@
     }
 
     @Override
+    public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) {
+        // NA as MediaSession2 doesn't support UserEngagementStates for FGS.
+    }
+
+    @Override
     public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
             KeyEvent ke, int sequenceId, ResultReceiver cb) {
         // TODO(jaewan): Implement.
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 0a9109b..0d779af 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -234,51 +234,49 @@
     private final Runnable mUserEngagementTimeoutExpirationRunnable =
             () -> {
                 synchronized (mLock) {
-                    updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+                    updateUserEngagedStateIfNeededLocked(
+                            /* isTimeoutExpired= */ true,
+                            /* isGlobalPrioritySessionActive= */ false);
                 }
             };
 
     @GuardedBy("mLock")
     private @UserEngagementState int mUserEngagementState = USER_DISENGAGED;
 
-    @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED})
+    @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARILY_ENGAGED, USER_DISENGAGED})
     @Retention(RetentionPolicy.SOURCE)
     private @interface UserEngagementState {}
 
     /**
-     * Indicates that the session is active and in one of the user engaged states.
+     * Indicates that the session is {@linkplain MediaSession#isActive() active} and in one of the
+     * {@linkplain PlaybackState#isActive() active states}.
      *
      * @see #updateUserEngagedStateIfNeededLocked(boolean)
      */
     private static final int USER_PERMANENTLY_ENGAGED = 0;
 
     /**
-     * Indicates that the session is active and in {@link PlaybackState#STATE_PAUSED} state.
+     * Indicates that the session is {@linkplain MediaSession#isActive() active} and has recently
+     * switched to one of the {@linkplain PlaybackState#isActive() inactive states}.
      *
      * @see #updateUserEngagedStateIfNeededLocked(boolean)
      */
-    private static final int USER_TEMPORARY_ENGAGED = 1;
+    private static final int USER_TEMPORARILY_ENGAGED = 1;
 
     /**
-     * Indicates that the session is either not active or in one of the user disengaged states
+     * Indicates that the session is either not {@linkplain MediaSession#isActive() active} or in
+     * one of the {@linkplain PlaybackState#isActive() inactive states}.
      *
      * @see #updateUserEngagedStateIfNeededLocked(boolean)
      */
     private static final int USER_DISENGAGED = 2;
 
     /**
-     * Indicates the duration of the temporary engaged states, in milliseconds.
+     * Indicates the duration of the temporary engaged state, in milliseconds.
      *
-     * <p>Some {@link MediaSession} states like {@link PlaybackState#STATE_PAUSED} are temporarily
-     * engaged, meaning the corresponding session is only considered in an engaged state for the
-     * duration of this timeout, and only if coming from an engaged state.
-     *
-     * <p>For example, if a session is transitioning from a user-engaged state {@link
-     * PlaybackState#STATE_PLAYING} to a temporary user-engaged state {@link
-     * PlaybackState#STATE_PAUSED}, then the session will be considered in a user-engaged state for
-     * the duration of this timeout, starting at the transition instant. However, a temporary
-     * user-engaged state is not considered user-engaged when transitioning from a non-user engaged
-     * state {@link PlaybackState#STATE_STOPPED}.
+     * <p>When switching to an {@linkplain PlaybackState#isActive() inactive state}, the user is
+     * treated as temporarily engaged, meaning the corresponding session is only considered in an
+     * engaged state for the duration of this timeout, and only if coming from an engaged state.
      */
     private static final int TEMP_USER_ENGAGED_TIMEOUT_MS = 600000;
 
@@ -604,7 +602,8 @@
             mSessionCb.mCb.asBinder().unlinkToDeath(this, 0);
             mDestroyed = true;
             mPlaybackState = null;
-            updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+            updateUserEngagedStateIfNeededLocked(
+                    /* isTimeoutExpired= */ true, /* isGlobalPrioritySessionActive= */ false);
             mHandler.post(MessageHandler.MSG_DESTROYED);
         }
     }
@@ -621,6 +620,24 @@
         mHandler.post(mUserEngagementTimeoutExpirationRunnable);
     }
 
+    @Override
+    public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) {
+        mHandler.post(
+                () -> {
+                    synchronized (mLock) {
+                        if (isGlobalPrioritySessionActive) {
+                            mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable);
+                        } else {
+                            if (mUserEngagementState == USER_TEMPORARILY_ENGAGED) {
+                                mHandler.postDelayed(
+                                        mUserEngagementTimeoutExpirationRunnable,
+                                        TEMP_USER_ENGAGED_TIMEOUT_MS);
+                            }
+                        }
+                    }
+                });
+    }
+
     /**
      * Sends media button.
      *
@@ -1111,21 +1128,20 @@
     }
 
     @GuardedBy("mLock")
-    private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) {
+    private void updateUserEngagedStateIfNeededLocked(
+            boolean isTimeoutExpired, boolean isGlobalPrioritySessionActive) {
         if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
             return;
         }
         int oldUserEngagedState = mUserEngagementState;
         int newUserEngagedState;
-        if (!isActive() || mPlaybackState == null || mDestroyed) {
+        if (!isActive() || mPlaybackState == null) {
             newUserEngagedState = USER_DISENGAGED;
-        } else if (isActive() && mPlaybackState.isActive()) {
+        } else if (mPlaybackState.isActive()) {
             newUserEngagedState = USER_PERMANENTLY_ENGAGED;
-        } else if (mPlaybackState.getState() == PlaybackState.STATE_PAUSED) {
-            newUserEngagedState =
-                    oldUserEngagedState == USER_PERMANENTLY_ENGAGED || !isTimeoutExpired
-                            ? USER_TEMPORARY_ENGAGED
-                            : USER_DISENGAGED;
+        } else if (oldUserEngagedState == USER_PERMANENTLY_ENGAGED
+                || (oldUserEngagedState == USER_TEMPORARILY_ENGAGED && !isTimeoutExpired)) {
+            newUserEngagedState = USER_TEMPORARILY_ENGAGED;
         } else {
             newUserEngagedState = USER_DISENGAGED;
         }
@@ -1134,7 +1150,7 @@
         }
 
         mUserEngagementState = newUserEngagedState;
-        if (newUserEngagedState == USER_TEMPORARY_ENGAGED) {
+        if (newUserEngagedState == USER_TEMPORARILY_ENGAGED && !isGlobalPrioritySessionActive) {
             mHandler.postDelayed(
                     mUserEngagementTimeoutExpirationRunnable, TEMP_USER_ENGAGED_TIMEOUT_MS);
         } else {
@@ -1189,9 +1205,11 @@
                         .logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK,
                                 callingUid, callingPid);
             }
+            boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive();
             synchronized (mLock) {
                 mIsActive = active;
-                updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
+                updateUserEngagedStateIfNeededLocked(
+                        /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive);
             }
             long token = Binder.clearCallingIdentity();
             try {
@@ -1348,9 +1366,11 @@
             boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState)
                     || (!TRANSITION_PRIORITY_STATES.contains(oldState)
                     && TRANSITION_PRIORITY_STATES.contains(newState));
+            boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive();
             synchronized (mLock) {
                 mPlaybackState = state;
-                updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
+                updateUserEngagedStateIfNeededLocked(
+                        /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive);
             }
             final long token = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 15f90d4..6c3b123 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -206,6 +206,10 @@
      */
     public abstract void expireTempEngaged();
 
+    /** Notifies record that the global priority session active state changed. */
+    public abstract void onGlobalPrioritySessionActiveChanged(
+            boolean isGlobalPrioritySessionActive);
+
     @Override
     public final boolean equals(Object o) {
         if (this == o) return true;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 1ebc856..2b29fbd 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -362,6 +362,7 @@
                                     + record.isActive());
                 }
                 user.pushAddressedPlayerChangedLocked();
+                mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
             } else {
                 if (!user.mPriorityStack.contains(record)) {
                     Log.w(TAG, "Unknown session updated. Ignoring.");
@@ -394,11 +395,16 @@
 
     // Currently only media1 can become global priority session.
     void setGlobalPrioritySession(MediaSessionRecord record) {
+        boolean globalPrioritySessionActiveChanged = false;
         synchronized (mLock) {
             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
             if (mGlobalPrioritySession != record) {
                 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
                         + " to " + record);
+                globalPrioritySessionActiveChanged =
+                        (mGlobalPrioritySession == null && record.isActive())
+                                || (mGlobalPrioritySession != null
+                                        && mGlobalPrioritySession.isActive() != record.isActive());
                 mGlobalPrioritySession = record;
                 if (user != null && user.mPriorityStack.contains(record)) {
                     // Handle the global priority session separately.
@@ -409,6 +415,30 @@
                 }
             }
         }
+        if (globalPrioritySessionActiveChanged) {
+            mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
+        }
+    }
+
+    /** Returns whether the global priority session is active. */
+    boolean isGlobalPrioritySessionActive() {
+        synchronized (mLock) {
+            return isGlobalPriorityActiveLocked();
+        }
+    }
+
+    private void notifyGlobalPrioritySessionActiveChanged() {
+        if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+            return;
+        }
+        synchronized (mLock) {
+            boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
+            for (Set<MediaSessionRecordImpl> records : mUserEngagedSessionsForFgs.values()) {
+                for (MediaSessionRecordImpl record : records) {
+                    record.onGlobalPrioritySessionActiveChanged(isGlobalPriorityActive);
+                }
+            }
+        }
     }
 
     private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
@@ -646,8 +676,11 @@
 
         if (mGlobalPrioritySession == session) {
             mGlobalPrioritySession = null;
-            if (session.isActive() && user != null) {
-                user.pushAddressedPlayerChangedLocked();
+            if (session.isActive()) {
+                if (user != null) {
+                    user.pushAddressedPlayerChangedLocked();
+                }
+                mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
             }
         } else {
             if (user != null) {
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 4310231..63cd59e 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -34,4 +34,5 @@
 
 # Files related to activity security
 per-file ActivityStarter.java = file:/ACTIVITY_SECURITY_OWNERS
+per-file ActivityStartController.java = file:/ACTIVITY_SECURITY_OWNERS
 per-file ActivityTaskManagerService.java = file:/ACTIVITY_SECURITY_OWNERS
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 7a710dc..662f916 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -55,7 +55,6 @@
         "com_android_server_powerstats_PowerStatsService.cpp",
         "com_android_server_power_stats_CpuPowerStatsCollector.cpp",
         "com_android_server_hint_HintManagerService.cpp",
-        "com_android_server_SerialService.cpp",
         "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
         "com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp",
         "com_android_server_stats_pull_StatsPullAtomService.cpp",
diff --git a/services/core/jni/com_android_server_SerialService.cpp b/services/core/jni/com_android_server_SerialService.cpp
deleted file mode 100644
index 6600c98..0000000
--- a/services/core/jni/com_android_server_SerialService.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open 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.
- */
-
-#define LOG_TAG "SerialServiceJNI"
-#include "utils/Log.h"
-
-#include "jni.h"
-#include <nativehelper/JNIPlatformHelp.h>
-#include "android_runtime/AndroidRuntime.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-namespace android
-{
-
-static struct parcel_file_descriptor_offsets_t
-{
-    jclass mClass;
-    jmethodID mConstructor;
-} gParcelFileDescriptorOffsets;
-
-static jobject android_server_SerialService_open(JNIEnv *env, jobject /* thiz */, jstring path)
-{
-    const char *pathStr = env->GetStringUTFChars(path, NULL);
-
-    int fd = open(pathStr, O_RDWR | O_NOCTTY);
-    if (fd < 0) {
-        ALOGE("could not open %s", pathStr);
-        env->ReleaseStringUTFChars(path, pathStr);
-        return NULL;
-    }
-    env->ReleaseStringUTFChars(path, pathStr);
-
-    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
-    if (fileDescriptor == NULL) {
-        close(fd);
-        return NULL;
-    }
-    return env->NewObject(gParcelFileDescriptorOffsets.mClass,
-        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
-}
-
-
-static const JNINativeMethod method_table[] = {
-    { "native_open",                "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
-                                    (void*)android_server_SerialService_open },
-};
-
-int register_android_server_SerialService(JNIEnv *env)
-{
-    jclass clazz = env->FindClass("com/android/server/SerialService");
-    if (clazz == NULL) {
-        ALOGE("Can't find com/android/server/SerialService");
-        return -1;
-    }
-
-    clazz = env->FindClass("android/os/ParcelFileDescriptor");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
-    gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
-    gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
-    LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
-                 "Unable to find constructor for android.os.ParcelFileDescriptor");
-
-    return jniRegisterNativeMethods(env, "com/android/server/SerialService",
-            method_table, NELEM(method_table));
-}
-
-};
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 6464081..f9633e2 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -33,7 +33,6 @@
 int register_android_server_power_stats_CpuPowerStatsCollector(JNIEnv* env);
 int register_android_server_HintManagerService(JNIEnv* env);
 int register_android_server_storage_AppFuse(JNIEnv* env);
-int register_android_server_SerialService(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_UsbAlsaJackDetector(JNIEnv* env);
 int register_android_server_UsbAlsaMidiDevice(JNIEnv* env);
@@ -93,7 +92,6 @@
     register_android_server_PowerStatsService(env);
     register_android_server_power_stats_CpuPowerStatsCollector(env);
     register_android_server_HintManagerService(env);
-    register_android_server_SerialService(env);
     register_android_server_InputManager(env);
     register_android_server_LightsService(env);
     register_android_server_UsbDeviceManager(env);
diff --git a/services/tests/mockingservicestests/src/com/android/server/OWNERS b/services/tests/mockingservicestests/src/com/android/server/OWNERS
index dc5cb8d6..0c9f70c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/OWNERS
@@ -1,6 +1,6 @@
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppStateTracker* = file:/apex/jobscheduler/OWNERS
-per-file *DeviceIdleController* = file:/apex/jobscheduler/OWNERS
+per-file *DeviceIdleController* = file:/apex/jobscheduler/DEVICE_IDLE_OWNERS
 per-file SensitiveContentProtectionManagerService* = file:/core/java/android/permission/OWNERS
 per-file RescuePartyTest.java = file:/packages/CrashRecovery/OWNERS
 per-file *Storage* = file:/core/java/android/os/storage/OWNERS