Merge changes from topic "stopmediafgs" into main

* changes:
  am: Add set-media-foreground-service command.
  ActivityManagerService: Add method to set media fgs inactive.
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f80121d..d9f8d33 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -1150,6 +1150,20 @@
     public abstract void stopForegroundServiceDelegate(@NonNull ServiceConnection connection);
 
     /**
+     * Notifies that a media foreground service associated with a media session has
+     * transitioned to a "user-disengaged" state.
+     * Upon receiving this notification, service may be removed from the foreground state. It
+     * should only be called by {@link com.android.server.media.MediaSessionService}
+     *
+     * @param packageName The package name of the app running the media foreground service.
+     * @param userId The user ID associated with the foreground service.
+     * @param notificationId The ID of the media notification associated with the foreground
+     *                      service.
+     */
+    public abstract void notifyInactiveMediaForegroundService(@NonNull String packageName,
+            @UserIdInt int userId, int notificationId);
+
+    /**
      * Same as {@link android.app.IActivityManager#startProfile(int userId)}, but it would succeed
      * even if the profile is disabled - it should only be called by
      * {@link com.android.server.devicepolicy.DevicePolicyManagerService} when starting a profile
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 3f540ad..ab3ab15 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -115,6 +115,7 @@
 import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
 import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__REQUEST_TYPE__BIND;
 import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__REQUEST_TYPE__START;
+import static com.android.media.flags.Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
@@ -9319,6 +9320,46 @@
         }
     }
 
+    /**
+     * Handles notifications from MediaSessionService about inactive media foreground services.
+     * This method evaluates the provided information and determines whether to stop the
+     * corresponding foreground service.
+     *
+     * @param packageName The package name of the app running the foreground service.
+     * @param userId The user ID associated with the foreground service.
+     * @param notificationId The ID of the media notification associated with the foreground
+     *                      service.
+     */
+    void notifyInactiveMediaForegroundServiceLocked(@NonNull String packageName,
+            @UserIdInt int userId, int notificationId) {
+        if (!enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+            return;
+        }
+
+        final ServiceMap smap = mServiceMap.get(userId);
+        if (smap == null) {
+            return;
+        }
+        final int serviceSize = smap.mServicesByInstanceName.size();
+        for (int i = 0; i < serviceSize; i++) {
+            final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i);
+            if (sr.appInfo.packageName.equals(packageName) && sr.isForeground) {
+                if (sr.foregroundServiceType
+                        == ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
+                        && sr.foregroundId == notificationId) {
+                    if (DEBUG_FOREGROUND_SERVICE) {
+                        Slog.d(TAG, "Forcing media foreground service to background for package "
+                                + packageName);
+                    }
+                    setServiceForegroundInnerLocked(sr, /* id */ 0,
+                            /* notification */ null, /* flags */ 0,
+                            /* foregroundServiceType */ 0, /* callingUidStart */ 0);
+                }
+            }
+        }
+    }
+
+
     private static void getClientPackages(ServiceRecord sr, ArraySet<String> output) {
         var connections = sr.getConnections();
         for (int conni = connections.size() - 1; conni >= 0; conni--) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e166807..5c5361b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17923,6 +17923,15 @@
         }
 
         @Override
+        public void notifyInactiveMediaForegroundService(@NonNull String packageName,
+                @UserIdInt int userId, int notificationId) {
+            synchronized (ActivityManagerService.this) {
+                mServices.notifyInactiveMediaForegroundServiceLocked(packageName, userId,
+                        notificationId);
+            }
+        }
+
+        @Override
         public ArraySet<String> getClientPackages(String servicePackageName) {
             synchronized (ActivityManagerService.this) {
                 return mServices.getClientPackagesLocked(servicePackageName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 02e2c39..d3d3fc9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -39,6 +39,8 @@
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.media.flags.Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange;
+import static com.android.media.flags.Flags.FLAG_ENABLE_NOTIFYING_ACTIVITY_MANAGER_WITH_MEDIA_SESSION_STATUS_CHANGE;
 import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -445,6 +447,8 @@
                     return runCapabilities(pw);
                 case "set-app-zygote-preload-timeout":
                     return runSetAppZygotePreloadTimeout(pw);
+                case "set-media-foreground-service":
+                    return runSetMediaForegroundService(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -454,6 +458,48 @@
         return -1;
     }
 
+    int runSetMediaForegroundService(PrintWriter pw) throws RemoteException {
+        mInternal.enforceCallingPermission(
+                android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE,
+                "runSetMediaForegroundService()");
+        final PrintWriter err = getErrPrintWriter();
+        if (!enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+            err.println("Error: flag "
+                    + FLAG_ENABLE_NOTIFYING_ACTIVITY_MANAGER_WITH_MEDIA_SESSION_STATUS_CHANGE
+                    + " not enabled");
+            return -1;
+        }
+        int userId = UserHandle.USER_CURRENT;
+        final String cmd = getNextArgRequired();
+        if ("inactive".equals(cmd)) {
+            String opt;
+            while ((opt = getNextOption()) != null) {
+                if (opt.equals("--user")) {
+                    userId = UserHandle.parseUserArg(getNextArgRequired());
+                    if (userId == UserHandle.USER_ALL) {
+                        err.println(
+                                "Error: Can't set media fgs inactive with user 'all'");
+                        return -1;
+                    }
+                } else {
+                    err.println("Error: Unknown option: " + opt);
+                    return -1;
+                }
+            }
+            final String pkgName = getNextArgRequired();
+            final int notificationId = Integer.parseInt(getNextArgRequired());
+            if (notificationId == 0) {
+                err.println("Error: notification id cannot be zero");
+                return -1;
+            }
+            mInternal.mInternal.notifyInactiveMediaForegroundService(pkgName,
+                    userId, notificationId);
+            return 0;
+        }
+        err.println("Error: Unknown set-media-foreground-service command: " + cmd);
+        return -1;
+    }
+
     int runSetAppZygotePreloadTimeout(PrintWriter pw) throws RemoteException {
         final String timeout = getNextArgRequired();
         final int timeoutMs = Integer.parseInt(timeout);
@@ -4637,6 +4683,9 @@
             pw.println("         --protobuf: format output using protobuffer");
             pw.println("  set-app-zygote-preload-timeout <TIMEOUT_IN_MS>");
             pw.println("         Set the timeout for preloading code in the app-zygote");
+            pw.println("  set-media-foreground-service inactive [--user USER_ID]"
+                    + " <PACKAGE> <NOTIFICATION_ID>");
+            pw.println("         Set an app's media foreground service inactive.");
             Intent.printIntentArgsHelp(pw, "");
         }
     }