summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2020-11-18 17:22:45 -0800
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-04-08 16:40:05 +0000
commitf59cdacd1dcaa8220c01a66a26db37fb00c75982 (patch)
treede2a9fed48b904f742866c022badba96ff5f461c
parentaca94478ab543cb00679c6890f39ae7d1814d6ae (diff)
downloadbase-f59cdacd1dcaa8220c01a66a26db37fb00c75982.tar.gz
DO NOT MERGE - Disallow deletion of channels with FGS notifications
Bug: 156090809 Test: atest CtsAppTestCases:NotificationManagerTest Test: atest CtsAppTestCases:android.app.cts.ServiceTest Change-Id: I1c2bb78d86f194585d273661cecf3419f51965df Merged-In: I1c2bb78d86f194585d273661cecf3419f51965df (cherry picked from commit cfd88a8e244c2f89a1a6502e9683deb17692491d)
-rw-r--r--core/java/android/app/ActivityManagerInternal.java15
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java40
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java16
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java36
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java5
5 files changed, 107 insertions, 5 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 1d5f49a879d5..df0d7107c7a2 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -403,6 +403,21 @@ public abstract class ActivityManagerInternal {
public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
/**
+ * 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, 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, int userId,
+ String channelId);
+
+ /**
* This enforces {@code func} can only be called if either the caller is Recents activity or
* has {@code permission}.
*/
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b49d07f3e46c..43a3fc822b01 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -29,6 +29,7 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
@@ -367,6 +368,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.mServicesByName.size(); i++) {
+ final ServiceRecord sr = smap.mServicesByName.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.mServicesByName.size(); i++) {
+ final ServiceRecord sr = smap.mServicesByName.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) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ae240bdd1c01..edbff464474a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -26837,6 +26837,22 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@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 cancelRecentsAnimation(boolean restoreHomeStackPosition) {
ActivityManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c422f5e092bc..bfa1b5fc58c9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -307,6 +307,7 @@ public class NotificationManagerService extends SystemService {
private IActivityManager mAm;
private ActivityManager mActivityManager;
+ private ActivityManagerInternal mAmi;
private IPackageManager mPackageManager;
private PackageManager mPackageManagerClient;
AudioManager mAudioManager;
@@ -1373,7 +1374,8 @@ public class NotificationManagerService extends SystemService {
ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
NotificationUsageStats usageStats, AtomicFile policyFile,
ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
- UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm) {
+ UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm,
+ ActivityManagerInternal ami) {
Resources resources = getContext().getResources();
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1390,6 +1392,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;
@@ -1530,7 +1533,8 @@ public class NotificationManagerService extends SystemService {
(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
getGroupHelper(), ActivityManager.getService(),
LocalServices.getService(UsageStatsManagerInternal.class),
- LocalServices.getService(DevicePolicyManagerInternal.class));
+ LocalServices.getService(DevicePolicyManagerInternal.class),
+ LocalServices.getService(ActivityManagerInternal.class));
// register for various Intents
IntentFilter filter = new IntentFilter();
@@ -2277,15 +2281,32 @@ public class NotificationManagerService extends SystemService {
return mRankingHelper.getNotificationChannel(pkg, uid, channelId, 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)) {
+ // Would be a behavioral change to introduce a throw here, so
+ // we simply return without affecting the channel.
+ 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);
mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(callingUid),
@@ -2317,13 +2338,20 @@ public class NotificationManagerService extends SystemService {
NotificationChannelGroup groupToDelete =
mRankingHelper.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 =
mRankingHelper.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),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 8b719de98f04..049c9b1a8415 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -63,6 +63,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
@@ -169,6 +170,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private AudioManager mAudioManager;
@Mock
ActivityManager mActivityManager;
+ @Mock
+ ActivityManagerInternal mAmi;
NotificationManagerService.WorkerHandler mHandler;
@Mock
Resources mResources;
@@ -283,7 +286,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mListeners, mAssistants, mConditionProviders,
mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
mGroupHelper, mAm, mAppUsageStats,
- mock(DevicePolicyManagerInternal.class));
+ mock(DevicePolicyManagerInternal.class), mAmi);
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;