Skip to content

Commit 2a33098

Browse files
author
Christopher Tate
committed
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 39b3890268913bc2dc8b90671d042c0e9b4090d2)
1 parent 64394de commit 2a33098

6 files changed

Lines changed: 114 additions & 11 deletions

File tree

core/java/android/app/ActivityManagerInternal.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,21 @@ public abstract void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int st
377377
*/
378378
public abstract boolean hasRunningForegroundService(int uid, int foregroundServiceType);
379379

380+
/**
381+
* Returns {@code true} if the given notification channel currently has a
382+
* notification associated with a foreground service. This is an AMS check
383+
* because that is the source of truth for the FGS state.
384+
*/
385+
public abstract boolean hasForegroundServiceNotification(String pkg, @UserIdInt int userId,
386+
String channelId);
387+
388+
/**
389+
* If the given app has any FGSs whose notifications are in the given channel,
390+
* stop them.
391+
*/
392+
public abstract void stopForegroundServicesForChannel(String pkg, @UserIdInt int userId,
393+
String channelId);
394+
380395
/**
381396
* Registers the specified {@code processObserver} to be notified of future changes to
382397
* process state.

services/core/java/com/android/server/am/ActiveServices.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
import java.util.ArrayList;
120120
import java.util.Comparator;
121121
import java.util.List;
122+
import java.util.Objects;
122123
import java.util.Set;
123124
import java.util.function.Predicate;
124125

@@ -433,6 +434,45 @@ boolean hasBackgroundServicesLocked(int callingUser) {
433434
return smap != null ? smap.mStartingBackground.size() >= mMaxStartingBackground : false;
434435
}
435436

437+
boolean hasForegroundServiceNotificationLocked(String pkg, int userId, String channelId) {
438+
final ServiceMap smap = mServiceMap.get(userId);
439+
if (smap != null) {
440+
for (int i = 0; i < smap.mServicesByInstanceName.size(); i++) {
441+
final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i);
442+
if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) {
443+
if (Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) {
444+
if (DEBUG_FOREGROUND_SERVICE) {
445+
Slog.d(TAG_SERVICE, "Channel u" + userId + "/pkg=" + pkg
446+
+ "/channelId=" + channelId
447+
+ " has fg service notification");
448+
}
449+
return true;
450+
}
451+
}
452+
}
453+
}
454+
return false;
455+
}
456+
457+
void stopForegroundServicesForChannelLocked(String pkg, int userId, String channelId) {
458+
final ServiceMap smap = mServiceMap.get(userId);
459+
if (smap != null) {
460+
for (int i = 0; i < smap.mServicesByInstanceName.size(); i++) {
461+
final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i);
462+
if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) {
463+
if (Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) {
464+
if (DEBUG_FOREGROUND_SERVICE) {
465+
Slog.d(TAG_SERVICE, "Stopping FGS u" + userId + "/pkg=" + pkg
466+
+ "/channelId=" + channelId
467+
+ " for conversation channel clear");
468+
}
469+
stopServiceLocked(sr);
470+
}
471+
}
472+
}
473+
}
474+
}
475+
436476
private ServiceMap getServiceMapLocked(int callingUser) {
437477
ServiceMap smap = mServiceMap.get(callingUser);
438478
if (smap == null) {

services/core/java/com/android/server/am/ActivityManagerService.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19718,6 +19718,22 @@ public boolean hasRunningForegroundService(int uid, int foregroundServicetype) {
1971819718
return false;
1971919719
}
1972019720

19721+
@Override
19722+
public boolean hasForegroundServiceNotification(String pkg, int userId,
19723+
String channelId) {
19724+
synchronized (ActivityManagerService.this) {
19725+
return mServices.hasForegroundServiceNotificationLocked(pkg, userId, channelId);
19726+
}
19727+
}
19728+
19729+
@Override
19730+
public void stopForegroundServicesForChannel(String pkg, int userId,
19731+
String channelId) {
19732+
synchronized (ActivityManagerService.this) {
19733+
mServices.stopForegroundServicesForChannelLocked(pkg, userId, channelId);
19734+
}
19735+
}
19736+
1972119737
@Override
1972219738
public void registerProcessObserver(IProcessObserver processObserver) {
1972319739
ActivityManagerService.this.registerProcessObserver(processObserver);

services/core/java/com/android/server/notification/NotificationManagerService.java

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ public class NotificationManagerService extends SystemService {
403403
private IActivityManager mAm;
404404
private ActivityTaskManagerInternal mAtm;
405405
private ActivityManager mActivityManager;
406+
private ActivityManagerInternal mAmi;
406407
private IPackageManager mPackageManager;
407408
private PackageManager mPackageManagerClient;
408409
AudioManager mAudioManager;
@@ -1876,7 +1877,7 @@ void init(WorkerHandler handler, RankingHandler rankingHandler,
18761877
DevicePolicyManagerInternal dpm, IUriGrantsManager ugm,
18771878
UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager,
18781879
NotificationHistoryManager historyManager, StatsManager statsManager,
1879-
TelephonyManager telephonyManager) {
1880+
TelephonyManager telephonyManager, ActivityManagerInternal ami) {
18801881
mHandler = handler;
18811882
Resources resources = getContext().getResources();
18821883
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -1897,6 +1898,7 @@ void init(WorkerHandler handler, RankingHandler rankingHandler,
18971898
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
18981899
mCompanionManager = companionManager;
18991900
mActivityManager = activityManager;
1901+
mAmi = ami;
19001902
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
19011903
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
19021904
mDpm = dpm;
@@ -2119,7 +2121,8 @@ null, snoozeHelper, new NotificationUsageStats(getContext()),
21192121
new NotificationHistoryManager(getContext(), handler),
21202122
mStatsManager = (StatsManager) getContext().getSystemService(
21212123
Context.STATS_MANAGER),
2122-
getContext().getSystemService(TelephonyManager.class));
2124+
getContext().getSystemService(TelephonyManager.class),
2125+
LocalServices.getService(ActivityManagerInternal.class));
21232126

21242127
// register for various Intents
21252128
IntentFilter filter = new IntentFilter();
@@ -3405,15 +3408,30 @@ public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
34053408
pkg, uid, channelId, conversationId, true, includeDeleted);
34063409
}
34073410

3411+
// Returns 'true' if the given channel has a notification associated
3412+
// with an active foreground service.
3413+
private void enforceDeletingChannelHasNoFgService(String pkg, int userId,
3414+
String channelId) {
3415+
if (mAmi.hasForegroundServiceNotification(pkg, userId, channelId)) {
3416+
Slog.w(TAG, "Package u" + userId + "/" + pkg
3417+
+ " may not delete notification channel '"
3418+
+ channelId + "' with fg service");
3419+
throw new SecurityException("Not allowed to delete channel " + channelId
3420+
+ " with a foreground service");
3421+
}
3422+
}
3423+
34083424
@Override
34093425
public void deleteNotificationChannel(String pkg, String channelId) {
34103426
checkCallerIsSystemOrSameApp(pkg);
34113427
final int callingUid = Binder.getCallingUid();
3428+
final int callingUser = UserHandle.getUserId(callingUid);
34123429
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
34133430
throw new IllegalArgumentException("Cannot delete default channel");
34143431
}
3432+
enforceDeletingChannelHasNoFgService(pkg, callingUser, channelId);
34153433
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
3416-
UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
3434+
callingUser, REASON_CHANNEL_BANNED, null);
34173435
mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId);
34183436
mListeners.notifyNotificationChannelChanged(pkg,
34193437
UserHandle.getUserHandleForUid(callingUid),
@@ -3426,19 +3444,23 @@ public void deleteNotificationChannel(String pkg, String channelId) {
34263444
public void deleteConversationNotificationChannels(String pkg, int uid,
34273445
String conversationId) {
34283446
checkCallerIsSystem();
3429-
final int callingUid = Binder.getCallingUid();
34303447
List<NotificationChannel> channels =
34313448
mPreferencesHelper.getNotificationChannelsByConversationId(
34323449
pkg, uid, conversationId);
34333450
if (!channels.isEmpty()) {
3451+
// Preflight for fg service notifications in these channels: do nothing
3452+
// unless they're all eligible
3453+
final int appUserId = UserHandle.getUserId(uid);
34343454
for (NotificationChannel nc : channels) {
3455+
final String channelId = nc.getId();
3456+
mAmi.stopForegroundServicesForChannel(pkg, appUserId, channelId);
34353457
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, nc.getId(), 0, 0, true,
3436-
UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
3437-
mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, nc.getId());
3458+
appUserId, REASON_CHANNEL_BANNED, null);
3459+
mPreferencesHelper.deleteNotificationChannel(pkg, uid, channelId);
34383460
mListeners.notifyNotificationChannelChanged(pkg,
3439-
UserHandle.getUserHandleForUid(callingUid),
3461+
UserHandle.getUserHandleForUid(uid),
34403462
mPreferencesHelper.getNotificationChannel(
3441-
pkg, callingUid, nc.getId(), true),
3463+
pkg, uid, channelId, true),
34423464
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
34433465
}
34443466
handleSavePolicyFile();
@@ -3469,13 +3491,20 @@ public void deleteNotificationChannelGroup(String pkg, String groupId) {
34693491
NotificationChannelGroup groupToDelete =
34703492
mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
34713493
if (groupToDelete != null) {
3494+
// Preflight for allowability
3495+
final int userId = UserHandle.getUserId(callingUid);
3496+
List<NotificationChannel> groupChannels = groupToDelete.getChannels();
3497+
for (int i = 0; i < groupChannels.size(); i++) {
3498+
enforceDeletingChannelHasNoFgService(pkg, userId,
3499+
groupChannels.get(i).getId());
3500+
}
34723501
List<NotificationChannel> deletedChannels =
34733502
mPreferencesHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
34743503
for (int i = 0; i < deletedChannels.size(); i++) {
34753504
final NotificationChannel deletedChannel = deletedChannels.get(i);
34763505
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
34773506
true,
3478-
UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
3507+
userId, REASON_CHANNEL_BANNED,
34793508
null);
34803509
mListeners.notifyNotificationChannelChanged(pkg,
34813510
UserHandle.getUserHandleForUid(callingUid),

services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,8 @@ null, new ComponentName(PKG, "test_class"),
499499
mGroupHelper, mAm, mAtm, mAppUsageStats,
500500
mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
501501
mAppOpsManager, mUm, mHistoryManager, mStatsManager,
502-
mock(TelephonyManager.class));
502+
mock(TelephonyManager.class),
503+
mock(ActivityManagerInternal.class));
503504
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
504505

505506
mService.setAudioManager(mAudioManager);

services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import static org.mockito.Mockito.when;
3333

3434
import android.app.ActivityManager;
35+
import android.app.ActivityManagerInternal;
3536
import android.app.AppOpsManager;
3637
import android.app.IActivityManager;
3738
import android.app.IUriGrantsManager;
@@ -156,7 +157,8 @@ public void setUp() throws Exception {
156157
mock(DevicePolicyManagerInternal.class), mock(IUriGrantsManager.class),
157158
mock(UriGrantsManagerInternal.class),
158159
mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class),
159-
mock(StatsManager.class), mock(TelephonyManager.class));
160+
mock(StatsManager.class), mock(TelephonyManager.class),
161+
mock(ActivityManagerInternal.class));
160162
} catch (SecurityException e) {
161163
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
162164
throw e;

0 commit comments

Comments
 (0)