Loading core/java/android/app/ActivityManagerInternal.java +15 −0 Original line number Diff line number Diff line Loading @@ -377,6 +377,21 @@ public abstract class ActivityManagerInternal { */ public abstract boolean hasRunningForegroundService(int uid, int foregroundServiceType); /** * Returns {@code true} if the given notification channel currently has a * notification associated with a foreground service. This is an AMS check * because that is the source of truth for the FGS state. */ public abstract boolean hasForegroundServiceNotification(String pkg, @UserIdInt int userId, String channelId); /** * If the given app has any FGSs whose notifications are in the given channel, * stop them. */ public abstract void stopForegroundServicesForChannel(String pkg, @UserIdInt int userId, String channelId); /** * Registers the specified {@code processObserver} to be notified of future changes to * process state. Loading services/core/java/com/android/server/am/ActiveServices.java +40 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,7 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; Loading Loading @@ -433,6 +434,45 @@ public final class ActiveServices { return smap != null ? smap.mStartingBackground.size() >= mMaxStartingBackground : false; } boolean hasForegroundServiceNotificationLocked(String pkg, int userId, String channelId) { final ServiceMap smap = mServiceMap.get(userId); if (smap != null) { for (int i = 0; i < smap.mServicesByInstanceName.size(); i++) { final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i); if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) { if (Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG_SERVICE, "Channel u" + userId + "/pkg=" + pkg + "/channelId=" + channelId + " has fg service notification"); } return true; } } } } return false; } void stopForegroundServicesForChannelLocked(String pkg, int userId, String channelId) { final ServiceMap smap = mServiceMap.get(userId); if (smap != null) { for (int i = 0; i < smap.mServicesByInstanceName.size(); i++) { final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i); if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) { if (Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG_SERVICE, "Stopping FGS u" + userId + "/pkg=" + pkg + "/channelId=" + channelId + " for conversation channel clear"); } stopServiceLocked(sr); } } } } } private ServiceMap getServiceMapLocked(int callingUser) { ServiceMap smap = mServiceMap.get(callingUser); if (smap == null) { Loading services/core/java/com/android/server/am/ActivityManagerService.java +16 −0 Original line number Diff line number Diff line Loading @@ -19728,6 +19728,22 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } @Override public boolean hasForegroundServiceNotification(String pkg, int userId, String channelId) { synchronized (ActivityManagerService.this) { return mServices.hasForegroundServiceNotificationLocked(pkg, userId, channelId); } } @Override public void stopForegroundServicesForChannel(String pkg, int userId, String channelId) { synchronized (ActivityManagerService.this) { mServices.stopForegroundServicesForChannelLocked(pkg, userId, channelId); } } @Override public void registerProcessObserver(IProcessObserver processObserver) { ActivityManagerService.this.registerProcessObserver(processObserver); services/core/java/com/android/server/notification/NotificationManagerService.java +38 −9 Original line number Diff line number Diff line Loading @@ -403,6 +403,7 @@ public class NotificationManagerService extends SystemService { private IActivityManager mAm; private ActivityTaskManagerInternal mAtm; private ActivityManager mActivityManager; private ActivityManagerInternal mAmi; private IPackageManager mPackageManager; private PackageManager mPackageManagerClient; AudioManager mAudioManager; Loading Loading @@ -1876,7 +1877,7 @@ public class NotificationManagerService extends SystemService { DevicePolicyManagerInternal dpm, IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager, NotificationHistoryManager historyManager, StatsManager statsManager, TelephonyManager telephonyManager) { TelephonyManager telephonyManager, ActivityManagerInternal ami) { mHandler = handler; Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), Loading @@ -1897,6 +1898,7 @@ public class NotificationManagerService extends SystemService { mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); mCompanionManager = companionManager; mActivityManager = activityManager; mAmi = ami; mDeviceIdleController = IDeviceIdleController.Stub.asInterface( ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); mDpm = dpm; Loading Loading @@ -2119,7 +2121,8 @@ public class NotificationManagerService extends SystemService { new NotificationHistoryManager(getContext(), handler), mStatsManager = (StatsManager) getContext().getSystemService( Context.STATS_MANAGER), getContext().getSystemService(TelephonyManager.class)); getContext().getSystemService(TelephonyManager.class), LocalServices.getService(ActivityManagerInternal.class)); // register for various Intents IntentFilter filter = new IntentFilter(); Loading Loading @@ -3405,15 +3408,30 @@ public class NotificationManagerService extends SystemService { pkg, uid, channelId, conversationId, true, includeDeleted); } // Returns 'true' if the given channel has a notification associated // with an active foreground service. private void enforceDeletingChannelHasNoFgService(String pkg, int userId, String channelId) { if (mAmi.hasForegroundServiceNotification(pkg, userId, channelId)) { Slog.w(TAG, "Package u" + userId + "/" + pkg + " may not delete notification channel '" + channelId + "' with fg service"); throw new SecurityException("Not allowed to delete channel " + channelId + " with a foreground service"); } } @Override public void deleteNotificationChannel(String pkg, String channelId) { checkCallerIsSystemOrSameApp(pkg); final int callingUid = Binder.getCallingUid(); final int callingUser = UserHandle.getUserId(callingUid); if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { throw new IllegalArgumentException("Cannot delete default channel"); } enforceDeletingChannelHasNoFgService(pkg, callingUser, channelId); cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true, UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null); callingUser, REASON_CHANNEL_BANNED, null); mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId); mListeners.notifyNotificationChannelChanged(pkg, UserHandle.getUserHandleForUid(callingUid), Loading @@ -3426,19 +3444,23 @@ public class NotificationManagerService extends SystemService { public void deleteConversationNotificationChannels(String pkg, int uid, String conversationId) { checkCallerIsSystem(); final int callingUid = Binder.getCallingUid(); List<NotificationChannel> channels = mPreferencesHelper.getNotificationChannelsByConversationId( pkg, uid, conversationId); if (!channels.isEmpty()) { // Preflight for fg service notifications in these channels: do nothing // unless they're all eligible final int appUserId = UserHandle.getUserId(uid); for (NotificationChannel nc : channels) { final String channelId = nc.getId(); mAmi.stopForegroundServicesForChannel(pkg, appUserId, channelId); cancelAllNotificationsInt(MY_UID, MY_PID, pkg, nc.getId(), 0, 0, true, UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null); mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, nc.getId()); appUserId, REASON_CHANNEL_BANNED, null); mPreferencesHelper.deleteNotificationChannel(pkg, uid, channelId); mListeners.notifyNotificationChannelChanged(pkg, UserHandle.getUserHandleForUid(callingUid), UserHandle.getUserHandleForUid(uid), mPreferencesHelper.getNotificationChannel( pkg, callingUid, nc.getId(), true), pkg, uid, channelId, true), NOTIFICATION_CHANNEL_OR_GROUP_DELETED); } handleSavePolicyFile(); Loading Loading @@ -3469,13 +3491,20 @@ public class NotificationManagerService extends SystemService { NotificationChannelGroup groupToDelete = mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, callingUid); if (groupToDelete != null) { // Preflight for allowability final int userId = UserHandle.getUserId(callingUid); List<NotificationChannel> groupChannels = groupToDelete.getChannels(); for (int i = 0; i < groupChannels.size(); i++) { enforceDeletingChannelHasNoFgService(pkg, userId, groupChannels.get(i).getId()); } List<NotificationChannel> deletedChannels = mPreferencesHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId); for (int i = 0; i < deletedChannels.size(); i++) { final NotificationChannel deletedChannel = deletedChannels.get(i); cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0, true, UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, userId, REASON_CHANNEL_BANNED, null); mListeners.notifyNotificationChannelChanged(pkg, UserHandle.getUserHandleForUid(callingUid), Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -505,7 +505,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mGroupHelper, mAm, mAtm, mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class)); mock(TelephonyManager.class), mock(ActivityManagerInternal.class)); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); mService.setAudioManager(mAudioManager); Loading Loading
core/java/android/app/ActivityManagerInternal.java +15 −0 Original line number Diff line number Diff line Loading @@ -377,6 +377,21 @@ public abstract class ActivityManagerInternal { */ public abstract boolean hasRunningForegroundService(int uid, int foregroundServiceType); /** * Returns {@code true} if the given notification channel currently has a * notification associated with a foreground service. This is an AMS check * because that is the source of truth for the FGS state. */ public abstract boolean hasForegroundServiceNotification(String pkg, @UserIdInt int userId, String channelId); /** * If the given app has any FGSs whose notifications are in the given channel, * stop them. */ public abstract void stopForegroundServicesForChannel(String pkg, @UserIdInt int userId, String channelId); /** * Registers the specified {@code processObserver} to be notified of future changes to * process state. Loading
services/core/java/com/android/server/am/ActiveServices.java +40 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,7 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; Loading Loading @@ -433,6 +434,45 @@ public final class ActiveServices { return smap != null ? smap.mStartingBackground.size() >= mMaxStartingBackground : false; } boolean hasForegroundServiceNotificationLocked(String pkg, int userId, String channelId) { final ServiceMap smap = mServiceMap.get(userId); if (smap != null) { for (int i = 0; i < smap.mServicesByInstanceName.size(); i++) { final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i); if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) { if (Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG_SERVICE, "Channel u" + userId + "/pkg=" + pkg + "/channelId=" + channelId + " has fg service notification"); } return true; } } } } return false; } void stopForegroundServicesForChannelLocked(String pkg, int userId, String channelId) { final ServiceMap smap = mServiceMap.get(userId); if (smap != null) { for (int i = 0; i < smap.mServicesByInstanceName.size(); i++) { final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i); if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) { if (Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG_SERVICE, "Stopping FGS u" + userId + "/pkg=" + pkg + "/channelId=" + channelId + " for conversation channel clear"); } stopServiceLocked(sr); } } } } } private ServiceMap getServiceMapLocked(int callingUser) { ServiceMap smap = mServiceMap.get(callingUser); if (smap == null) { Loading
services/core/java/com/android/server/am/ActivityManagerService.java +16 −0 Original line number Diff line number Diff line Loading @@ -19728,6 +19728,22 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } @Override public boolean hasForegroundServiceNotification(String pkg, int userId, String channelId) { synchronized (ActivityManagerService.this) { return mServices.hasForegroundServiceNotificationLocked(pkg, userId, channelId); } } @Override public void stopForegroundServicesForChannel(String pkg, int userId, String channelId) { synchronized (ActivityManagerService.this) { mServices.stopForegroundServicesForChannelLocked(pkg, userId, channelId); } } @Override public void registerProcessObserver(IProcessObserver processObserver) { ActivityManagerService.this.registerProcessObserver(processObserver);
services/core/java/com/android/server/notification/NotificationManagerService.java +38 −9 Original line number Diff line number Diff line Loading @@ -403,6 +403,7 @@ public class NotificationManagerService extends SystemService { private IActivityManager mAm; private ActivityTaskManagerInternal mAtm; private ActivityManager mActivityManager; private ActivityManagerInternal mAmi; private IPackageManager mPackageManager; private PackageManager mPackageManagerClient; AudioManager mAudioManager; Loading Loading @@ -1876,7 +1877,7 @@ public class NotificationManagerService extends SystemService { DevicePolicyManagerInternal dpm, IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager, NotificationHistoryManager historyManager, StatsManager statsManager, TelephonyManager telephonyManager) { TelephonyManager telephonyManager, ActivityManagerInternal ami) { mHandler = handler; Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), Loading @@ -1897,6 +1898,7 @@ public class NotificationManagerService extends SystemService { mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); mCompanionManager = companionManager; mActivityManager = activityManager; mAmi = ami; mDeviceIdleController = IDeviceIdleController.Stub.asInterface( ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); mDpm = dpm; Loading Loading @@ -2119,7 +2121,8 @@ public class NotificationManagerService extends SystemService { new NotificationHistoryManager(getContext(), handler), mStatsManager = (StatsManager) getContext().getSystemService( Context.STATS_MANAGER), getContext().getSystemService(TelephonyManager.class)); getContext().getSystemService(TelephonyManager.class), LocalServices.getService(ActivityManagerInternal.class)); // register for various Intents IntentFilter filter = new IntentFilter(); Loading Loading @@ -3405,15 +3408,30 @@ public class NotificationManagerService extends SystemService { pkg, uid, channelId, conversationId, true, includeDeleted); } // Returns 'true' if the given channel has a notification associated // with an active foreground service. private void enforceDeletingChannelHasNoFgService(String pkg, int userId, String channelId) { if (mAmi.hasForegroundServiceNotification(pkg, userId, channelId)) { Slog.w(TAG, "Package u" + userId + "/" + pkg + " may not delete notification channel '" + channelId + "' with fg service"); throw new SecurityException("Not allowed to delete channel " + channelId + " with a foreground service"); } } @Override public void deleteNotificationChannel(String pkg, String channelId) { checkCallerIsSystemOrSameApp(pkg); final int callingUid = Binder.getCallingUid(); final int callingUser = UserHandle.getUserId(callingUid); if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { throw new IllegalArgumentException("Cannot delete default channel"); } enforceDeletingChannelHasNoFgService(pkg, callingUser, channelId); cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true, UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null); callingUser, REASON_CHANNEL_BANNED, null); mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId); mListeners.notifyNotificationChannelChanged(pkg, UserHandle.getUserHandleForUid(callingUid), Loading @@ -3426,19 +3444,23 @@ public class NotificationManagerService extends SystemService { public void deleteConversationNotificationChannels(String pkg, int uid, String conversationId) { checkCallerIsSystem(); final int callingUid = Binder.getCallingUid(); List<NotificationChannel> channels = mPreferencesHelper.getNotificationChannelsByConversationId( pkg, uid, conversationId); if (!channels.isEmpty()) { // Preflight for fg service notifications in these channels: do nothing // unless they're all eligible final int appUserId = UserHandle.getUserId(uid); for (NotificationChannel nc : channels) { final String channelId = nc.getId(); mAmi.stopForegroundServicesForChannel(pkg, appUserId, channelId); cancelAllNotificationsInt(MY_UID, MY_PID, pkg, nc.getId(), 0, 0, true, UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null); mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, nc.getId()); appUserId, REASON_CHANNEL_BANNED, null); mPreferencesHelper.deleteNotificationChannel(pkg, uid, channelId); mListeners.notifyNotificationChannelChanged(pkg, UserHandle.getUserHandleForUid(callingUid), UserHandle.getUserHandleForUid(uid), mPreferencesHelper.getNotificationChannel( pkg, callingUid, nc.getId(), true), pkg, uid, channelId, true), NOTIFICATION_CHANNEL_OR_GROUP_DELETED); } handleSavePolicyFile(); Loading Loading @@ -3469,13 +3491,20 @@ public class NotificationManagerService extends SystemService { NotificationChannelGroup groupToDelete = mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, callingUid); if (groupToDelete != null) { // Preflight for allowability final int userId = UserHandle.getUserId(callingUid); List<NotificationChannel> groupChannels = groupToDelete.getChannels(); for (int i = 0; i < groupChannels.size(); i++) { enforceDeletingChannelHasNoFgService(pkg, userId, groupChannels.get(i).getId()); } List<NotificationChannel> deletedChannels = mPreferencesHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId); for (int i = 0; i < deletedChannels.size(); i++) { final NotificationChannel deletedChannel = deletedChannels.get(i); cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0, true, UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, userId, REASON_CHANNEL_BANNED, null); mListeners.notifyNotificationChannelChanged(pkg, UserHandle.getUserHandleForUid(callingUid), Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -505,7 +505,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mGroupHelper, mAm, mAtm, mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class)); mock(TelephonyManager.class), mock(ActivityManagerInternal.class)); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); mService.setAudioManager(mAudioManager); Loading