Merge "Remove notification permission feature flag" into tm-dev am: af7982277b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18164160

Change-Id: Ib8aaa5d29fb04be54d2378d68ca42a8a46d332f0
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Julia Reynolds
2022-05-09 21:13:38 +00:00
committed by Automerger Merge Worker
21 changed files with 429 additions and 2136 deletions

View File

@@ -314,7 +314,6 @@ package android.app {
public final class NotificationChannel implements android.os.Parcelable {
method public int getOriginalImportance();
method public boolean isImportanceLockedByCriticalDeviceFunction();
method public boolean isImportanceLockedByOEM();
method public void lockFields(int);
method public void setDeleted(boolean);
method public void setDeletedTimeMs(long);

View File

@@ -863,14 +863,6 @@ public final class NotificationChannel implements Parcelable {
mImportanceLockedDefaultApp = locked;
}
/**
* @hide
*/
@TestApi
public boolean isImportanceLockedByOEM() {
return mImportanceLockedByOEM;
}
/**
* @hide
*/

View File

@@ -10154,15 +10154,6 @@ public final class Settings {
@Readable
public static final String NOTIFICATION_DISMISS_RTL = "notification_dismiss_rtl";
/**
* Whether the app-level notification setting is represented by a manifest permission.
*
* @hide
*/
@Readable
public static final String NOTIFICATION_PERMISSION_ENABLED =
"notification_permission_enabled";
/**
* Comma separated list of QS tiles that have been auto-added already.
* @hide

View File

@@ -5509,16 +5509,7 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 209;
}
if (currentVersion == 209) {
// Version 209: Enable enforcement of
// android.Manifest.permission#POST_NOTIFICATIONS in order for applications
// to post notifications.
final SettingsState secureSettings = getSecureSettingsLocked(userId);
secureSettings.insertSettingLocked(
Secure.NOTIFICATION_PERMISSION_ENABLED,
/* enabled= */ "1",
/* tag= */ null,
/* makeDefault= */ false,
SettingsState.SYSTEM_PACKAGE_NAME);
// removed now that feature is enabled for everyone
currentVersion = 210;
}

View File

@@ -160,8 +160,7 @@ class ChannelEditorDialogController @Inject constructor(
val channels = groupList
.flatMap { group ->
group.channels.asSequence().filterNot { channel ->
channel.isImportanceLockedByOEM ||
channel.importance == IMPORTANCE_NONE ||
channel.importance == IMPORTANCE_NONE ||
channel.isImportanceLockedByCriticalDeviceFunction
}
}

View File

@@ -376,44 +376,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* calls.
*/
private static Boolean isSystemNotification(Context context, StatusBarNotification sbn) {
// TODO (b/194833441): clean up before launch
if (Settings.Secure.getIntForUser(context.getContentResolver(),
Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1) {
INotificationManager iNm = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
INotificationManager iNm = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
boolean isSystem = false;
try {
isSystem = iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId());
} catch (RemoteException e) {
Log.e(TAG, "cannot reach NMS");
}
RoleManager rm = context.getSystemService(RoleManager.class);
List<String> fixedRoleHolders = new ArrayList<>();
fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_DIALER));
fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_EMERGENCY));
if (fixedRoleHolders.contains(sbn.getPackageName())) {
isSystem = true;
}
return isSystem;
} else {
PackageManager packageManager = CentralSurfaces.getPackageManagerForUser(
context, sbn.getUser().getIdentifier());
Boolean isSystemNotification = null;
try {
PackageInfo packageInfo = packageManager.getPackageInfo(
sbn.getPackageName(), PackageManager.GET_SIGNATURES);
isSystemNotification =
com.android.settingslib.Utils.isSystemPackage(
context.getResources(), packageManager, packageInfo);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "cacheIsSystemNotification: Could not find package info");
}
return isSystemNotification;
boolean isSystem = false;
try {
isSystem = iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId());
} catch (RemoteException e) {
Log.e(TAG, "cannot reach NMS");
}
RoleManager rm = context.getSystemService(RoleManager.class);
List<String> fixedRoleHolders = new ArrayList<>();
fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_DIALER));
fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_EMERGENCY));
if (fixedRoleHolders.contains(sbn.getPackageName())) {
isSystem = true;
}
return isSystem;
}
public NotificationContentView[] getLayouts() {
@@ -567,9 +547,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mEntry.mIsSystemNotification = isSystemNotification(mContext, mEntry.getSbn());
}
// TODO (b/194833441): remove when we've migrated to permission
boolean isNonblockable = mEntry.getChannel().isImportanceLockedByOEM()
|| mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction();
boolean isNonblockable = mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction();
if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) {
if (mEntry.mIsSystemNotification) {

View File

@@ -317,15 +317,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
assertFalse(row.getIsNonblockable());
}
@Test
public void testGetIsNonblockable_oemLocked() throws Exception {
ExpandableNotificationRow row =
mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
row.getEntry().getChannel().setImportanceLockedByOEM(true);
assertTrue(row.getIsNonblockable());
}
@Test
public void testGetIsNonblockable_criticalDeviceFunction() throws Exception {
ExpandableNotificationRow row =

View File

@@ -656,10 +656,10 @@ public class NotificationManagerService extends SystemService {
private int mWarnRemoteViewsSizeBytes;
private int mStripRemoteViewsSizeBytes;
final boolean mEnableAppSettingMigration;
private boolean mForceUserSetOnUpgrade;
private MetricsLogger mMetricsLogger;
private NotificationChannelLogger mNotificationChannelLogger;
private TriPredicate<String, Integer, String> mAllowedManagedServicePackages;
private final SavePolicyFileRunnable mSavePolicyFile = new SavePolicyFileRunnable();
@@ -1999,12 +1999,6 @@ public class NotificationManagerService extends SystemService {
mNotificationRecordLogger = notificationRecordLogger;
mNotificationInstanceIdSequence = notificationInstanceIdSequence;
Notification.processAllowlistToken = ALLOWLIST_TOKEN;
// TODO (b/194833441): remove when OS is ready for migration. This flag is checked once
// rather than having a settings observer because some of the behaviors (e.g. readXml) only
// happen on reboot
mEnableAppSettingMigration = Settings.Secure.getIntForUser(
getContext().getContentResolver(),
Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1;
}
// TODO - replace these methods with new fields in the VisibleForTesting constructor
@@ -2184,7 +2178,7 @@ public class NotificationManagerService extends SystemService {
TelephonyManager telephonyManager, ActivityManagerInternal ami,
MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper,
UsageStatsManagerInternal usageStatsManagerInternal,
TelecomManager telecomManager) {
TelecomManager telecomManager, NotificationChannelLogger channelLogger) {
mHandler = handler;
Resources resources = getContext().getResources();
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -2281,12 +2275,13 @@ public class NotificationManagerService extends SystemService {
}
});
mPermissionHelper = permissionHelper;
mNotificationChannelLogger = channelLogger;
mPreferencesHelper = new PreferencesHelper(getContext(),
mPackageManagerClient,
mRankingHandler,
mZenModeHelper,
mPermissionHelper,
new NotificationChannelLoggerImpl(),
mNotificationChannelLogger,
mAppOps,
new SysUiStatsEvent.BuilderFactory());
mRankingHelper = new RankingHelper(getContext(),
@@ -2368,9 +2363,6 @@ public class NotificationManagerService extends SystemService {
mNotificationEffectsEnabledForAutomotive =
resources.getBoolean(R.bool.config_enableServerNotificationEffectsForAutomotive);
mPreferencesHelper.lockChannelsForOEM(getContext().getResources().getStringArray(
com.android.internal.R.array.config_nonBlockableNotificationPackages));
mZenModeHelper.setPriorityOnlyDndExemptPackages(getContext().getResources().getStringArray(
com.android.internal.R.array.config_priorityOnlyDndExemptPackages));
@@ -2510,10 +2502,11 @@ public class NotificationManagerService extends SystemService {
LocalServices.getService(ActivityManagerInternal.class),
createToastRateLimiter(), new PermissionHelper(LocalServices.getService(
PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(),
AppGlobals.getPermissionManager(), mEnableAppSettingMigration,
AppGlobals.getPermissionManager(),
mForceUserSetOnUpgrade),
LocalServices.getService(UsageStatsManagerInternal.class),
getContext().getSystemService(TelecomManager.class));
getContext().getSystemService(TelecomManager.class),
new NotificationChannelLoggerImpl());
publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -2805,7 +2798,7 @@ public class NotificationManagerService extends SystemService {
}
}
private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
boolean fromListener) {
if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
// cancel
@@ -2827,11 +2820,9 @@ public class NotificationManagerService extends SystemService {
mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true);
if (mEnableAppSettingMigration) {
if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) {
mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid),
channel.getImportance() != IMPORTANCE_NONE, true);
}
if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) {
mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid),
channel.getImportance() != IMPORTANCE_NONE, true);
}
maybeNotifyChannelOwner(pkg, uid, preUpdate, channel);
@@ -3480,36 +3471,19 @@ public class NotificationManagerService extends SystemService {
@Override
public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
enforceSystemOrSystemUI("setNotificationsEnabledForPackage");
if (mEnableAppSettingMigration) {
boolean wasEnabled = mPermissionHelper.hasPermission(uid);
if (wasEnabled == enabled) {
return;
}
mPermissionHelper.setNotificationPermission(
pkg, UserHandle.getUserId(uid), enabled, true);
sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
} else {
synchronized (mNotificationLock) {
boolean wasEnabled = mPreferencesHelper.getImportance(pkg, uid)
!= NotificationManager.IMPORTANCE_NONE;
if (wasEnabled == enabled) {
return;
}
}
mPreferencesHelper.setEnabled(pkg, uid, enabled);
// TODO (b/194833441): this is being ignored by app ops now that the permission
// exists, so send the broadcast manually
mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
enabled ? MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
boolean wasEnabled = mPermissionHelper.hasPermission(uid);
if (wasEnabled == enabled) {
return;
}
mPermissionHelper.setNotificationPermission(
pkg, UserHandle.getUserId(uid), enabled, true);
sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
.setType(MetricsEvent.TYPE_ACTION)
.setPackageName(pkg)
.setSubtype(enabled ? 1 : 0));
mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled);
// Now, cancel any outstanding notifications that are part of a just-disabled app
if (!enabled) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
@@ -3535,8 +3509,6 @@ public class NotificationManagerService extends SystemService {
public void setNotificationsEnabledWithImportanceLockForPackage(
String pkg, int uid, boolean enabled) {
setNotificationsEnabledForPackage(pkg, uid, enabled);
mPreferencesHelper.setAppImportanceLocked(pkg, uid);
}
/**
@@ -3652,14 +3624,10 @@ public class NotificationManagerService extends SystemService {
@Override
public int getPackageImportance(String pkg) {
checkCallerIsSystemOrSameApp(pkg);
if (mEnableAppSettingMigration) {
if (mPermissionHelper.hasPermission(Binder.getCallingUid())) {
return IMPORTANCE_DEFAULT;
} else {
return IMPORTANCE_NONE;
}
if (mPermissionHelper.hasPermission(Binder.getCallingUid())) {
return IMPORTANCE_DEFAULT;
} else {
return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
return IMPORTANCE_NONE;
}
}
@@ -5891,8 +5859,7 @@ public class NotificationManagerService extends SystemService {
NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey,
boolean needsOngoingFlag) {
NotificationRecord summaryRecord = null;
boolean isPermissionFixed = mPermissionHelper.isMigrationEnabled()
? mPermissionHelper.isPermissionFixed(pkg, userId) : false;
boolean isPermissionFixed = mPermissionHelper.isPermissionFixed(pkg, userId);
synchronized (mNotificationLock) {
NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
if (notificationRecord == null) {
@@ -5901,10 +5868,6 @@ public class NotificationManagerService extends SystemService {
return null;
}
NotificationChannel channel = notificationRecord.getChannel();
boolean isImportanceFixed = mPermissionHelper.isMigrationEnabled()
? isPermissionFixed
: (channel.isImportanceLockedByOEM()
|| channel.isImportanceLockedByCriticalDeviceFunction());
final StatusBarNotification adjustedSbn = notificationRecord.getSbn();
userId = adjustedSbn.getUser().getIdentifier();
int uid = adjustedSbn.getUid();
@@ -5950,7 +5913,7 @@ public class NotificationManagerService extends SystemService {
System.currentTimeMillis());
summaryRecord = new NotificationRecord(getContext(), summarySbn,
notificationRecord.getChannel());
summaryRecord.setImportanceFixed(isImportanceFixed);
summaryRecord.setImportanceFixed(isPermissionFixed);
summaryRecord.setIsAppImportanceLocked(
notificationRecord.getIsAppImportanceLocked());
summaries.put(pkg, summarySbn.getKey());
@@ -5998,10 +5961,6 @@ public class NotificationManagerService extends SystemService {
@VisibleForTesting
protected ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>>
getAllUsersNotificationPermissions() {
// don't bother if migration is not enabled
if (!mEnableAppSettingMigration) {
return null;
}
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> allPermissions = new ArrayMap<>();
final List<UserInfo> allUsers = mUm.getUsers();
// for each of these, get the package notification permissions that are associated
@@ -6515,13 +6474,8 @@ public class NotificationManagerService extends SystemService {
+ ", notificationUid=" + notificationUid
+ ", notification=" + notification;
Slog.e(TAG, noChannelStr);
boolean appNotificationsOff;
if (mEnableAppSettingMigration) {
appNotificationsOff = !mPermissionHelper.hasPermission(notificationUid);
} else {
appNotificationsOff = mPreferencesHelper.getImportance(pkg, notificationUid)
== NotificationManager.IMPORTANCE_NONE;
}
boolean appNotificationsOff = !mPermissionHelper.hasPermission(notificationUid);
if (!appNotificationsOff) {
doChannelWarningToast(notificationUid,
@@ -6533,14 +6487,11 @@ public class NotificationManagerService extends SystemService {
}
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));
r.setIsAppImportanceLocked(mPermissionHelper.isPermissionUserSet(pkg, userId));
r.setPostSilently(postSilently);
r.setFlagBubbleRemoved(false);
r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg));
boolean isImportanceFixed = mPermissionHelper.isMigrationEnabled()
? mPermissionHelper.isPermissionFixed(pkg, userId)
: (channel.isImportanceLockedByOEM()
|| channel.isImportanceLockedByCriticalDeviceFunction());
boolean isImportanceFixed = mPermissionHelper.isPermissionFixed(pkg, userId);
r.setImportanceFixed(isImportanceFixed);
if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
@@ -6997,11 +6948,7 @@ public class NotificationManagerService extends SystemService {
}
private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) {
if (mEnableAppSettingMigration) {
return mPermissionHelper.hasPermission(uid);
} else {
return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
}
return mPermissionHelper.hasPermission(uid);
}
protected int getNotificationCount(String pkg, int userId, int excludedId,

View File

@@ -1089,7 +1089,7 @@ public final class NotificationRecord {
}
/**
* @see PreferencesHelper#getIsAppImportanceLocked(String, int)
* @see PermissionHelper#isPermissionUserSet(String, int)
*/
public boolean getIsAppImportanceLocked() {
return mIsAppImportanceLocked;

View File

@@ -55,30 +55,21 @@ public final class PermissionHelper {
private final PermissionManagerServiceInternal mPmi;
private final IPackageManager mPackageManager;
private final IPermissionManager mPermManager;
// TODO (b/194833441): Remove when the migration is enabled
private final boolean mMigrationEnabled;
private final boolean mForceUserSetOnUpgrade;
public PermissionHelper(PermissionManagerServiceInternal pmi, IPackageManager packageManager,
IPermissionManager permManager, boolean migrationEnabled,
boolean forceUserSetOnUpgrade) {
IPermissionManager permManager, boolean forceUserSetOnUpgrade) {
mPmi = pmi;
mPackageManager = packageManager;
mPermManager = permManager;
mMigrationEnabled = migrationEnabled;
mForceUserSetOnUpgrade = forceUserSetOnUpgrade;
}
public boolean isMigrationEnabled() {
return mMigrationEnabled;
}
/**
* Returns whether the given uid holds the notification permission. Must not be called
* with a lock held.
*/
public boolean hasPermission(int uid) {
assertFlag();
final long callingId = Binder.clearCallingIdentity();
try {
return mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(uid)
@@ -93,7 +84,6 @@ public final class PermissionHelper {
* Must not be called with a lock held. Format: uid, packageName
*/
Set<Pair<Integer, String>> getAppsRequestingPermission(int userId) {
assertFlag();
Set<Pair<Integer, String>> requested = new HashSet<>();
List<PackageInfo> pkgs = getInstalledPackages(userId);
for (PackageInfo pi : pkgs) {
@@ -131,7 +121,6 @@ public final class PermissionHelper {
* with a lock held. Format: uid, packageName.
*/
Set<Pair<Integer, String>> getAppsGrantedPermission(int userId) {
assertFlag();
Set<Pair<Integer, String>> granted = new HashSet<>();
ParceledListSlice<PackageInfo> parceledList = null;
try {
@@ -153,7 +142,6 @@ public final class PermissionHelper {
public @NonNull
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>>
getNotificationPermissionValues(int userId) {
assertFlag();
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> notifPermissions = new ArrayMap<>();
Set<Pair<Integer, String>> allRequestingUids = getAppsRequestingPermission(userId);
Set<Pair<Integer, String>> allApprovedUids = getAppsGrantedPermission(userId);
@@ -180,7 +168,6 @@ public final class PermissionHelper {
*/
public void setNotificationPermission(String packageName, @UserIdInt int userId, boolean grant,
boolean userSet, boolean reviewRequired) {
assertFlag();
final long callingId = Binder.clearCallingIdentity();
try {
// Do not change the permission if the package doesn't request it, do not change fixed
@@ -221,7 +208,6 @@ public final class PermissionHelper {
* restoring a pre-T backup on a T+ device
*/
public void setNotificationPermission(PackagePermission pkgPerm) {
assertFlag();
if (pkgPerm == null || pkgPerm.packageName == null) {
return;
}
@@ -233,7 +219,6 @@ public final class PermissionHelper {
}
public boolean isPermissionFixed(String packageName, @UserIdInt int userId) {
assertFlag();
final long callingId = Binder.clearCallingIdentity();
try {
try {
@@ -251,7 +236,6 @@ public final class PermissionHelper {
}
boolean isPermissionUserSet(String packageName, @UserIdInt int userId) {
assertFlag();
final long callingId = Binder.clearCallingIdentity();
try {
try {
@@ -269,7 +253,6 @@ public final class PermissionHelper {
}
boolean isPermissionGrantedByDefaultOrRole(String packageName, @UserIdInt int userId) {
assertFlag();
final long callingId = Binder.clearCallingIdentity();
try {
try {
@@ -288,7 +271,6 @@ public final class PermissionHelper {
private boolean packageRequestsNotificationPermission(String packageName,
@UserIdInt int userId) {
assertFlag();
try {
String[] permissions = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS,
userId).requestedPermissions;
@@ -299,12 +281,6 @@ public final class PermissionHelper {
return false;
}
private void assertFlag() {
if (!mMigrationEnabled) {
throw new IllegalStateException("Method called without checking flag value");
}
}
public static class PackagePermission {
public final String packageName;
public final @UserIdInt int userId;

View File

@@ -209,11 +209,7 @@ public class PreferencesHelper implements RankingConfig {
mAppOps = appOpsManager;
mStatsEventBuilderFactory = statsEventBuilderFactory;
if (mPermissionHelper.isMigrationEnabled()) {
XML_VERSION = 4;
} else {
XML_VERSION = 2;
}
XML_VERSION = 4;
updateBadgingEnabled();
updateBubblesEnabled();
@@ -230,8 +226,7 @@ public class PreferencesHelper implements RankingConfig {
final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1);
boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION)
&& mPermissionHelper.isMigrationEnabled();
boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION);
if (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION) {
// make a note that we should show the notification at some point.
// it shouldn't be possible for the user to already have seen it, as the XML version
@@ -393,8 +388,6 @@ public class PreferencesHelper implements RankingConfig {
hasUserConfiguredSettings(r));
pkgPerms.add(pkgPerm);
}
} else if (!mPermissionHelper.isMigrationEnabled()) {
r.importance = appImportance;
}
} catch (Exception e) {
Slog.w(TAG, "Failed to restore pkg", e);
@@ -417,16 +410,8 @@ public class PreferencesHelper implements RankingConfig {
} else {
channel.populateFromXml(parser);
}
if (!mPermissionHelper.isMigrationEnabled()) {
channel.setImportanceLockedByCriticalDeviceFunction(
r.defaultAppLockedImportance);
channel.setImportanceLockedByOEM(r.oemLockedImportance);
if (!channel.isImportanceLockedByOEM()) {
if (r.oemLockedChannels.contains(channel.getId())) {
channel.setImportanceLockedByOEM(true);
}
}
}
channel.setImportanceLockedByCriticalDeviceFunction(
r.defaultAppLockedImportance);
if (isShortcutOk(channel) && isDeletionOk(channel)) {
r.channels.put(id, channel);
@@ -604,7 +589,7 @@ public class PreferencesHelper implements RankingConfig {
out.endTag(null, TAG_STATUS_ICONS);
}
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> notifPermissions = new ArrayMap<>();
if (mPermissionHelper.isMigrationEnabled() && forBackup) {
if (forBackup) {
notifPermissions = mPermissionHelper.getNotificationPermissionValues(userId);
}
@@ -736,28 +721,6 @@ public class PreferencesHelper implements RankingConfig {
}
}
/**
* Gets importance.
*/
@Override
public int getImportance(String packageName, int uid) {
synchronized (mPackagePreferences) {
return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
}
}
/**
* Returns whether the importance of the corresponding notification is user-locked and shouldn't
* be adjusted by an assistant (via means of a blocking helper, for example). For the channel
* locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
*/
public boolean getIsAppImportanceLocked(String packageName, int uid) {
synchronized (mPackagePreferences) {
int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
}
}
@Override
public boolean canShowBadge(String packageName, int uid) {
synchronized (mPackagePreferences) {
@@ -1043,16 +1006,10 @@ public class PreferencesHelper implements RankingConfig {
: NotificationChannel.DEFAULT_ALLOW_BUBBLE);
}
clearLockedFieldsLocked(channel);
if (!mPermissionHelper.isMigrationEnabled()) {
channel.setImportanceLockedByOEM(r.oemLockedImportance);
if (!channel.isImportanceLockedByOEM()) {
if (r.oemLockedChannels.contains(channel.getId())) {
channel.setImportanceLockedByOEM(true);
}
}
channel.setImportanceLockedByCriticalDeviceFunction(
r.defaultAppLockedImportance);
}
channel.setImportanceLockedByCriticalDeviceFunction(
r.defaultAppLockedImportance);
if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
channel.setLockscreenVisibility(
NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
@@ -1133,33 +1090,15 @@ public class PreferencesHelper implements RankingConfig {
updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
}
if (mPermissionHelper.isMigrationEnabled()) {
if (mPermissionHelper.isPermissionFixed(r.pkg, UserHandle.getUserId(r.uid))
&& !(channel.isBlockable() || channel.getImportance() == IMPORTANCE_NONE)) {
updatedChannel.setImportance(channel.getImportance());
}
} else {
// no importance updates are allowed if OEM blocked it
updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
if (updatedChannel.isImportanceLockedByOEM()) {
updatedChannel.setImportance(channel.getImportance());
}
updatedChannel.setImportanceLockedByCriticalDeviceFunction(
r.defaultAppLockedImportance);
if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
&& updatedChannel.getImportance() == IMPORTANCE_NONE) {
updatedChannel.setImportance(channel.getImportance());
}
if ((mPermissionHelper.isPermissionFixed(r.pkg, UserHandle.getUserId(r.uid))
|| channel.isImportanceLockedByCriticalDeviceFunction())
&& !(channel.isBlockable() || channel.getImportance() == IMPORTANCE_NONE)) {
updatedChannel.setImportance(channel.getImportance());
}
r.channels.put(updatedChannel.getId(), updatedChannel);
if (onlyHasDefaultChannel(pkg, uid)) {
if (!mPermissionHelper.isMigrationEnabled()) {
// copy settings to app level so they are inherited by new channels
// when the app migrates
r.importance = updatedChannel.getImportance();
}
r.priority = updatedChannel.canBypassDnd()
? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
r.visibility = updatedChannel.getLockscreenVisibility();
@@ -1328,61 +1267,8 @@ public class PreferencesHelper implements RankingConfig {
mHideSilentStatusBarIcons = hide;
}
public void lockChannelsForOEM(String[] appOrChannelList) {
if (mPermissionHelper.isMigrationEnabled()) {
return;
}
if (appOrChannelList == null) {
return;
}
for (String appOrChannel : appOrChannelList) {
if (!TextUtils.isEmpty(appOrChannel)) {
String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
if (appSplit != null && appSplit.length > 0) {
String appName = appSplit[0];
String channelId = appSplit.length == 2 ? appSplit[1] : null;
synchronized (mPackagePreferences) {
boolean foundApp = false;
for (PackagePreferences r : mPackagePreferences.values()) {
if (r.pkg.equals(appName)) {
foundApp = true;
if (channelId == null) {
// lock all channels for the app
r.oemLockedImportance = true;
for (NotificationChannel channel : r.channels.values()) {
channel.setImportanceLockedByOEM(true);
}
} else {
NotificationChannel channel = r.channels.get(channelId);
if (channel != null) {
channel.setImportanceLockedByOEM(true);
}
// Also store the locked channels on the record, so they aren't
// temporarily lost when data is cleared on the package
r.oemLockedChannels.add(channelId);
}
}
}
if (!foundApp) {
List<String> channels =
mOemLockedApps.getOrDefault(appName, new ArrayList<>());
if (channelId != null) {
channels.add(channelId);
}
mOemLockedApps.put(appName, channels);
}
}
}
}
}
}
public void updateDefaultApps(int userId, ArraySet<String> toRemove,
ArraySet<Pair<String, Integer>> toAdd) {
if (mPermissionHelper.isMigrationEnabled()) {
return;
}
synchronized (mPackagePreferences) {
for (PackagePreferences p : mPackagePreferences.values()) {
if (userId == UserHandle.getUserId(p.uid)) {
@@ -1802,20 +1688,8 @@ public class PreferencesHelper implements RankingConfig {
}
for (int i = candidatePkgs.size() - 1; i >= 0; i--) {
Pair<String, Integer> app = candidatePkgs.valueAt(i);
if (mPermissionHelper.isMigrationEnabled()) {
if (!mPermissionHelper.hasPermission(app.second)) {
candidatePkgs.removeAt(i);
}
} else {
synchronized (mPackagePreferences) {
PackagePreferences r = getPackagePreferencesLocked(app.first, app.second);
if (r == null) {
continue;
}
if (r.importance == IMPORTANCE_NONE) {
candidatePkgs.removeAt(i);
}
}
if (!mPermissionHelper.hasPermission(app.second)) {
candidatePkgs.removeAt(i);
}
}
boolean haveBypassingApps = candidatePkgs.size() > 0;
@@ -1860,27 +1734,6 @@ public class PreferencesHelper implements RankingConfig {
return mAreChannelsBypassingDnd;
}
/**
* Sets importance.
*/
@Override
public void setImportance(String pkgName, int uid, int importance) {
synchronized (mPackagePreferences) {
getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
}
updateConfig();
}
public void setEnabled(String packageName, int uid, boolean enabled) {
boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
if (wasEnabled == enabled) {
return;
}
setImportance(packageName, uid,
enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
mNotificationChannelLogger.logAppNotificationsAllowed(uid, packageName, enabled);
}
/**
* Sets whether any notifications from the app, represented by the given {@code pkgName} and
* {@code uid}, have their importance locked by the user. Locked notifications don't get
@@ -2055,23 +1908,15 @@ public class PreferencesHelper implements RankingConfig {
pw.print(" (");
pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
pw.print(')');
if (!mPermissionHelper.isMigrationEnabled()) {
if (r.importance != DEFAULT_IMPORTANCE) {
pw.print(" importance=");
pw.print(NotificationListenerService.Ranking.importanceToString(
r.importance));
}
} else {
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
pw.print(" importance=");
pw.print(NotificationListenerService.Ranking.importanceToString(
packagePermissions.get(key).first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
pw.print(" userSet=");
pw.print(packagePermissions.get(key).second);
pkgsWithPermissionsToHandle.remove(key);
}
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
pw.print(" importance=");
pw.print(NotificationListenerService.Ranking.importanceToString(
packagePermissions.get(key).first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
pw.print(" userSet=");
pw.print(packagePermissions.get(key).second);
pkgsWithPermissionsToHandle.remove(key);
}
if (r.priority != DEFAULT_PRIORITY) {
pw.print(" priority=");
@@ -2111,7 +1956,7 @@ public class PreferencesHelper implements RankingConfig {
}
}
// Handle any remaining packages with permissions
if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
if (pkgsWithPermissionsToHandle != null) {
for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
// p.first is the uid of this package; p.second is the package name
if (filter.matches(p.second)) {
@@ -2151,16 +1996,12 @@ public class PreferencesHelper implements RankingConfig {
proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
proto.write(RankingHelperProto.RecordProto.UID, r.uid);
if (mPermissionHelper.isMigrationEnabled()) {
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
proto.write(RankingHelperProto.RecordProto.IMPORTANCE,
packagePermissions.get(key).first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
pkgsWithPermissionsToHandle.remove(key);
}
} else {
proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
proto.write(RankingHelperProto.RecordProto.IMPORTANCE,
packagePermissions.get(key).first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
pkgsWithPermissionsToHandle.remove(key);
}
proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
@@ -2177,7 +2018,7 @@ public class PreferencesHelper implements RankingConfig {
}
}
if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
if (pkgsWithPermissionsToHandle != null) {
for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
if (filter.matches(p.second)) {
fToken = proto.start(fieldId);
@@ -2217,25 +2058,22 @@ public class PreferencesHelper implements RankingConfig {
// collect whether this package's importance info was user-set for later, if needed
// before the migration is enabled, this will simply default to false in all cases.
boolean importanceIsUserSet = false;
if (mPermissionHelper.isMigrationEnabled()) {
// Even if this package's data is not present, we need to write something;
// so default to IMPORTANCE_NONE, since if PM doesn't know about the package
// for some reason, notifications are not allowed.
int importance = IMPORTANCE_NONE;
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
Pair<Boolean, Boolean> permissionPair = pkgPermissions.get(key);
importance = permissionPair.first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
// cache the second value for writing later
importanceIsUserSet = permissionPair.second;
// Even if this package's data is not present, we need to write something;
// so default to IMPORTANCE_NONE, since if PM doesn't know about the package
// for some reason, notifications are not allowed.
int importance = IMPORTANCE_NONE;
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
Pair<Boolean, Boolean> permissionPair = pkgPermissions.get(key);
importance = permissionPair.first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
// cache the second value for writing later
importanceIsUserSet = permissionPair.second;
pkgsWithPermissionsToHandle.remove(key);
}
event.writeInt(importance);
} else {
event.writeInt(r.importance);
pkgsWithPermissionsToHandle.remove(key);
}
event.writeInt(importance);
event.writeInt(r.visibility);
event.writeInt(r.lockedAppFields);
event.writeBoolean(importanceIsUserSet); // optional bool user_set_importance = 5;
@@ -2244,7 +2082,7 @@ public class PreferencesHelper implements RankingConfig {
}
// handle remaining packages with PackageManager permissions but not local settings
if (mPermissionHelper.isMigrationEnabled() && pkgPermissions != null) {
if (pkgPermissions != null) {
for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
if (pulledEvents > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
break;
@@ -2357,22 +2195,14 @@ public class PreferencesHelper implements RankingConfig {
try {
PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
PackagePreferences.put("packageName", r.pkg);
if (mPermissionHelper.isMigrationEnabled()) {
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (pkgPermissions != null
&& pkgsWithPermissionsToHandle.contains(key)) {
PackagePreferences.put("importance",
NotificationListenerService.Ranking.importanceToString(
pkgPermissions.get(key).first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
pkgsWithPermissionsToHandle.remove(key);
}
} else {
if (r.importance != DEFAULT_IMPORTANCE) {
PackagePreferences.put("importance",
NotificationListenerService.Ranking.importanceToString(
r.importance));
}
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (pkgPermissions != null
&& pkgsWithPermissionsToHandle.contains(key)) {
PackagePreferences.put("importance",
NotificationListenerService.Ranking.importanceToString(
pkgPermissions.get(key).first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
pkgsWithPermissionsToHandle.remove(key);
}
if (r.priority != DEFAULT_PRIORITY) {
PackagePreferences.put("priority",
@@ -2404,7 +2234,7 @@ public class PreferencesHelper implements RankingConfig {
}
// handle packages for which there are permissions but no local settings
if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
if (pkgsWithPermissionsToHandle != null) {
for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
if (filter == null || filter.matches(p.second)) {
JSONObject PackagePreferences = new JSONObject();
@@ -2443,8 +2273,7 @@ public class PreferencesHelper implements RankingConfig {
public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter,
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
JSONArray bans = new JSONArray();
Map<Integer, String> packageBans = mPermissionHelper.isMigrationEnabled()
? getPermissionBasedPackageBans(pkgPermissions) : getPackageBans();
Map<Integer, String> packageBans = getPermissionBasedPackageBans(pkgPermissions);
for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
final int userId = UserHandle.getUserId(ban.getKey());
final String packageName = ban.getValue();
@@ -2597,7 +2426,7 @@ public class PreferencesHelper implements RankingConfig {
synchronized (mPackagePreferences) {
mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
}
if (mPermissionHelper.isMigrationEnabled() && r.migrateToPm) {
if (r.migrateToPm) {
try {
PackagePermission p = new PackagePermission(
r.pkg, UserHandle.getUserId(r.uid),

View File

@@ -24,8 +24,6 @@ import java.util.Collection;
public interface RankingConfig {
void setImportance(String packageName, int uid, int importance);
int getImportance(String packageName, int uid);
void setShowBadge(String packageName, int uid, boolean showBadge);
boolean canShowBadge(String packageName, int uid);
boolean badgingEnabled(UserHandle userHandle);

View File

@@ -1453,16 +1453,6 @@ public final class PermissionPolicyService extends SystemService {
}
}
try {
if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, UserHandle.USER_SYSTEM)
== 0) {
return false;
}
} catch (Settings.SettingNotFoundException e) {
return false;
}
if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS)
|| CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, pkgName, user)
|| mKeyguardManager.isKeyguardLocked()) {

View File

@@ -82,8 +82,6 @@ public class ImportanceExtractorTest extends UiServiceTestCase {
ImportanceExtractor extractor = new ImportanceExtractor();
extractor.setConfig(mConfig);
when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
NotificationManager.IMPORTANCE_MIN);
NotificationChannel channel =
new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
@@ -101,8 +99,6 @@ public class ImportanceExtractorTest extends UiServiceTestCase {
ImportanceExtractor extractor = new ImportanceExtractor();
extractor.setConfig(mConfig);
when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
NotificationManager.IMPORTANCE_MIN);
NotificationChannel channel =
new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH);

View File

@@ -28,6 +28,13 @@ public class NotificationChannelLoggerFake implements NotificationChannelLogger
CallRecord(NotificationChannelEvent event) {
this.event = event;
}
@Override
public String toString() {
return "CallRecord{" +
"event=" + event +
'}';
}
}
private List<CallRecord> mCalls = new ArrayList<>();

View File

@@ -188,6 +188,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Pair;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -221,6 +222,7 @@ import com.android.server.wm.WindowManagerInternal;
import com.google.common.collect.ImmutableList;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -273,6 +275,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private WindowManagerInternal mWindowManagerInternal;
@Mock
private PermissionHelper mPermissionHelper;
private NotificationChannelLoggerFake mLogger = new NotificationChannelLoggerFake();
private TestableContext mContext = spy(getContext());
private final String PKG = mContext.getPackageName();
private TestableLooper mTestableLooper;
@@ -385,9 +388,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
"android.permission.WRITE_DEVICE_CONFIG",
"android.permission.READ_DEVICE_CONFIG",
"android.permission.READ_CONTACTS");
Settings.Secure.putIntForUser(
getContext().getContentResolver(),
Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM);
MockitoAnnotations.initMocks(this);
@@ -509,7 +509,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager,
mock(TelephonyManager.class),
mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class),
mTelecomManager);
mTelecomManager, mLogger);
// Return first true for RoleObserver main-thread check
when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
@@ -585,6 +585,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertNotNull(mBinderService.getNotificationChannel(
PKG, mContext.getUserId(), PKG, TEST_CHANNEL_ID));
clearInvocations(mRankingHandler);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
}
@After
@@ -1237,8 +1238,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testEnqueuedBlockedNotifications_blockedApp() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -1251,8 +1251,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testEnqueuedBlockedNotifications_blockedAppForegroundService() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -1344,6 +1343,30 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertEquals(0, mBinderService.getActiveNotifications(PKG).length);
}
@Test
public void testSetNotificationsEnabledForPackage_noChange() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, true);
verify(mPermissionHelper, never()).setNotificationPermission(
anyString(), anyInt(), anyBoolean(), anyBoolean());
}
@Test
public void testSetNotificationsEnabledForPackage() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, false);
verify(mPermissionHelper).setNotificationPermission(
mContext.getPackageName(), UserHandle.getUserId(mUid), false, true);
verify(mAppOpsManager, never()).setMode(anyInt(), anyInt(), anyString(), anyInt());
List<NotificationChannelLoggerFake.CallRecord> calls = mLogger.getCalls();
Assert.assertEquals(
NotificationChannelLogger.NotificationChannelEvent.APP_NOTIFICATIONS_BLOCKED,
calls.get(calls.size() -1).event);
}
@Test
public void testBlockedNotifications_blockedByAssistant() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
@@ -1371,7 +1394,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testBlockedNotifications_blockedByUser() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
@@ -1380,7 +1402,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationRecord r = generateNotificationRecord(channel);
mService.addEnqueuedNotification(r);
when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(IMPORTANCE_NONE);
when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
NotificationManagerService.PostNotificationRunnable runnable =
mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
@@ -1392,9 +1414,33 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mUsageStats, never()).registerPostedByApp(any());
}
@Test
public void testEnqueueNotificationInternal_noChannel() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
NotificationRecord nr = generateNotificationRecord(
new NotificationChannel("did not create", "", IMPORTANCE_DEFAULT));
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
verify(mPermissionHelper).hasPermission(mUid);
verify(mPermissionHelper, never()).hasPermission(Process.SYSTEM_UID);
reset(mPermissionHelper);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
verify(mPermissionHelper).hasPermission(mUid);
assertThat(mService.mChannelToastsSent).contains(mUid);
}
@Test
public void testEnqueueNotification_appBlocked() throws Exception {
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testEnqueueNotification_appBlocked", 0,
@@ -2688,6 +2734,49 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertEquals(0, notifs.length);
}
@Test
public void testDefaultChannelUpdatesApp_postMigrationToPermissions() throws Exception {
final NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
PKG_N_MR1, ActivityManager.getCurrentUser(), PKG_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID);
defaultChannel.setImportance(IMPORTANCE_NONE);
mBinderService.updateNotificationChannelForPackage(PKG_N_MR1, mUid, defaultChannel);
verify(mPermissionHelper).setNotificationPermission(
PKG_N_MR1, ActivityManager.getCurrentUser(), false, true);
}
@Test
public void testPostNotification_appPermissionFixed() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
when(mPermissionHelper.isPermissionFixed(PKG, 0)).thenReturn(true);
NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testPostNotification_appPermissionFixed", 0,
temp.getNotification(), 0);
waitForIdle();
assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(PKG);
assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
}
@Test
public void testSummaryNotification_appPermissionFixed() {
NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(temp);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true);
NotificationRecord r = mService.createAutoGroupSummary(
temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), false);
assertThat(r.isImportanceFixed()).isTrue();
}
@Test
public void testTvExtenderChannelOverride_onTv() throws Exception {
mService.setIsTelevision(true);
@@ -2721,13 +2810,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testUpdateAppNotifyCreatorBlock() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getImportance(PKG, mUid)).thenReturn(IMPORTANCE_DEFAULT);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
// should trigger a broadcast
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
Thread.sleep(500);
waitForIdle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
@@ -2739,7 +2827,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
mBinderService.setNotificationsEnabledForPackage(PKG, 0, false);
verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null));
@@ -2747,15 +2835,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testUpdateAppNotifyCreatorUnblock() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
// should not trigger a broadcast
when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_ALLOWED);
// should trigger a broadcast
mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
Thread.sleep(500);
waitForIdle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
@@ -4347,7 +4432,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertEquals(IMPORTANCE_LOW,
mService.getNotificationRecord(sbn.getKey()).getImportance());
assertEquals(IMPORTANCE_UNSPECIFIED, mBinderService.getPackageImportance(
assertEquals(IMPORTANCE_DEFAULT, mBinderService.getPackageImportance(
sbn.getPackageName()));
nb = new Notification.Builder(mContext)
@@ -4863,6 +4948,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testBackup() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
int systemChecks = mService.countSystemChecks;
when(mListeners.queryPackageForServices(anyString(), anyInt(), anyInt()))
.thenReturn(new ArraySet<>());
@@ -5966,8 +6052,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.thenReturn(false);
// notifications from this package are blocked by the user
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
setAppInForegroundForToasts(mUid, true);
@@ -6263,8 +6348,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.thenReturn(false);
// notifications from this package are blocked by the user
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
setAppInForegroundForToasts(mUid, false);
@@ -6350,8 +6434,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.thenReturn(true);
// notifications from this package are NOT blocked by the user
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_LOW);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
// enqueue toast -> no toasts enqueued
((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
@@ -6372,8 +6455,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.thenReturn(false);
// notifications from this package are blocked by the user
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
setAppInForegroundForToasts(mUid, false);
@@ -6396,8 +6478,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.thenReturn(true);
// notifications from this package ARE blocked by the user
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
setAppInForegroundForToasts(mUid, false);
@@ -7305,6 +7386,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
}
@Test
public void testAreNotificationsEnabledForPackage() throws Exception {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid);
verify(mPermissionHelper).hasPermission(mUid);
}
@Test
public void testAreNotificationsEnabledForPackage_crossUser() throws Exception {
try {
@@ -7314,21 +7403,31 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
} catch (SecurityException e) {
// pass
}
verify(mPermissionHelper, never()).hasPermission(anyInt());
// cross user, with permission, no problem
enableInteractAcrossUsers();
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid + UserHandle.PER_USER_RANGE);
verify(mPermissionHelper, never()).hasPermission(anyInt());
verify(mPermissionHelper).hasPermission(mUid + UserHandle.PER_USER_RANGE);
}
@Test
public void testAreNotificationsEnabledForPackage_viaInternalService() throws Exception {
assertEquals(mInternalService.areNotificationsEnabledForPackage(
mContext.getPackageName(), mUid),
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid));
verify(mPermissionHelper, never()).hasPermission(anyInt());
public void testAreNotificationsEnabledForPackage_viaInternalService() {
mInternalService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid);
verify(mPermissionHelper).hasPermission(mUid);
}
@Test
public void testGetPackageImportance() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
assertThat(mBinderService.getPackageImportance(mContext.getPackageName()))
.isEqualTo(IMPORTANCE_DEFAULT);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
assertThat(mBinderService.getPackageImportance(mContext.getPackageName()))
.isEqualTo(IMPORTANCE_NONE);
}
@Test
@@ -8977,49 +9076,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("test", 1002)));
}
@Test
public void testMigrationDisabledByDefault() {
assertThat(mService.mEnableAppSettingMigration).isFalse();
}
@Test
public void testPostNotification_channelLockedFixed() throws Exception {
mTestNotificationChannel.setImportanceLockedByOEM(true);
NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testPostNotification_appPermissionFixed", 0,
temp.getNotification(), 0);
waitForIdle();
assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(PKG);
assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
mBinderService.cancelAllNotifications(PKG, 0);
waitForIdle();
mTestNotificationChannel.setImportanceLockedByOEM(false);
mTestNotificationChannel.setImportanceLockedByCriticalDeviceFunction(true);
temp = generateNotificationRecord(mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testPostNotification_appPermissionFixed", 0,
temp.getNotification(), 0);
waitForIdle();
assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
notifs = mBinderService.getActiveNotifications(PKG);
assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
}
@Test
public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getImportance(PKG, mUid)).thenReturn(IMPORTANCE_NONE);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
assertThat(mBinderService.getNotificationChannelsBypassingDnd(PKG, mUid).getList())
.isEmpty();
verify(mPermissionHelper, never()).hasPermission(anyInt());
verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid);
}
@@ -9113,8 +9177,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mBinderService.setNotificationsEnabledForPackage(
r.getSbn().getPackageName(), r.getUid(), false);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
// normal blocked notifications - blocked
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
@@ -9151,6 +9214,67 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
}
@Test
public void testMediaNotificationsBypassBlock_atPost() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
Notification.Builder nb = new Notification.Builder(
mContext, mTestNotificationChannel.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.addAction(new Notification.Action.Builder(null, "test", null).build());
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
verify(mUsageStats).registerBlocked(any());
verify(mUsageStats, never()).registerPostedByApp(any());
// just using the style - blocked
mService.clearNotifications();
reset(mUsageStats);
nb.setStyle(new Notification.MediaStyle());
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addEnqueuedNotification(r);
runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
verify(mUsageStats).registerBlocked(any());
verify(mUsageStats, never()).registerPostedByApp(any());
// style + media session - bypasses block
mService.clearNotifications();
reset(mUsageStats);
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addEnqueuedNotification(r);
runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
verify(mUsageStats, never()).registerBlocked(any());
verify(mUsageStats).registerPostedByApp(any());
}
@Test
public void testCallNotificationsBypassBlock() throws Exception {
when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
@@ -9166,8 +9290,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mBinderService.setNotificationsEnabledForPackage(
r.getSbn().getPackageName(), r.getUid(), false);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
// normal blocked notifications - blocked
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
@@ -9312,9 +9435,37 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
public void testGetAllUsersNotificationPermissions_migrationNotEnabled() {
// make sure we don't bother if the migration is not enabled
assertThat(mService.getAllUsersNotificationPermissions()).isNull();
public void testGetAllUsersNotificationPermissions() {
// In this case, there are multiple users each with notification permissions (and also,
// for good measure, some without).
// make sure the collection returned contains info for all of them
final List<UserInfo> userInfos = new ArrayList<>();
userInfos.add(new UserInfo(0, "user0", 0));
userInfos.add(new UserInfo(1, "user1", 0));
userInfos.add(new UserInfo(2, "user2", 0));
when(mUm.getUsers()).thenReturn(userInfos);
// construct the permissions for each of them
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> permissions0 = new ArrayMap<>(),
permissions1 = new ArrayMap<>();
permissions0.put(new Pair<>(10, "package1"), new Pair<>(true, false));
permissions0.put(new Pair<>(20, "package2"), new Pair<>(false, true));
permissions1.put(new Pair<>(11, "package1"), new Pair<>(false, false));
permissions1.put(new Pair<>(21, "package2"), new Pair<>(true, true));
when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(permissions0);
when(mPermissionHelper.getNotificationPermissionValues(1)).thenReturn(permissions1);
when(mPermissionHelper.getNotificationPermissionValues(2)).thenReturn(new ArrayMap<>());
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> combinedPermissions =
mService.getAllUsersNotificationPermissions();
assertTrue(combinedPermissions.get(new Pair<>(10, "package1")).first);
assertFalse(combinedPermissions.get(new Pair<>(10, "package1")).second);
assertFalse(combinedPermissions.get(new Pair<>(20, "package2")).first);
assertTrue(combinedPermissions.get(new Pair<>(20, "package2")).second);
assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).first);
assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).second);
assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).first);
assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).second);
}
@Test

View File

@@ -1,877 +0,0 @@
/*
* Copyright (C) 2021 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.notification;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.PendingIntent.FLAG_MUTABLE;
import static android.app.PendingIntent.FLAG_ONE_SHOT;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_SYSTEM;
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.IUriGrantsManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.StatsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.media.AudioManager;
import android.media.session.MediaSession;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestablePermissions;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
import com.android.internal.app.IAppOpsService;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiServiceTestCase;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import com.android.server.notification.NotificationManagerService.NotificationListeners;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.quota.MultiRateLimiter;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
/**
* Tests that NMS reads/writes the app notification state from Package/PermissionManager when
* migration is enabled. Because the migration field is read onStart
* TODO (b/194833441): migrate these tests to NotificationManagerServiceTest when the migration is
* permanently enabled.
*/
public class NotificationPermissionMigrationTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private static final int UID_HEADLESS = 1000000;
private final int mUid = Binder.getCallingUid();
private TestableNotificationManagerService mService;
private INotificationManager mBinderService;
private NotificationManagerInternal mInternalService;
private ShortcutHelper mShortcutHelper;
@Mock
private IPackageManager mPackageManager;
@Mock
private PackageManager mPackageManagerClient;
@Mock
private PackageManagerInternal mPackageManagerInternal;
@Mock
private WindowManagerInternal mWindowManagerInternal;
@Mock
private PermissionHelper mPermissionHelper;
private TestableContext mContext = spy(getContext());
private final String PKG = mContext.getPackageName();
private TestableLooper mTestableLooper;
@Mock
private RankingHelper mRankingHelper;
@Mock private PreferencesHelper mPreferencesHelper;
AtomicFile mPolicyFile;
File mFile;
@Mock
private NotificationUsageStats mUsageStats;
@Mock
private UsageStatsManagerInternal mAppUsageStats;
@Mock
private AudioManager mAudioManager;
@Mock
private LauncherApps mLauncherApps;
@Mock
private ShortcutServiceInternal mShortcutServiceInternal;
@Mock
private UserManager mUserManager;
@Mock
ActivityManager mActivityManager;
@Mock
Resources mResources;
@Mock
RankingHandler mRankingHandler;
@Mock
ActivityManagerInternal mAmi;
@Mock
private Looper mMainLooper;
@Mock
IIntentSender pi1;
private static final int MAX_POST_DELAY = 1000;
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
private static final String VALID_CONVO_SHORTCUT_ID = "shortcut";
@Mock
private NotificationListeners mListeners;
@Mock
private NotificationListenerFilter mNlf;
@Mock private NotificationAssistants mAssistants;
@Mock private ConditionProviders mConditionProviders;
private ManagedServices.ManagedServiceInfo mListener;
@Mock private ICompanionDeviceManager mCompanionMgr;
@Mock SnoozeHelper mSnoozeHelper;
@Mock GroupHelper mGroupHelper;
@Mock
IBinder mPermOwner;
@Mock
IActivityManager mAm;
@Mock
ActivityTaskManagerInternal mAtm;
@Mock
IUriGrantsManager mUgm;
@Mock
UriGrantsManagerInternal mUgmInternal;
@Mock
AppOpsManager mAppOpsManager;
@Mock
private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback
mNotificationAssistantAccessGrantedCallback;
@Mock
UserManager mUm;
@Mock
NotificationHistoryManager mHistoryManager;
@Mock
StatsManager mStatsManager;
@Mock
AlarmManager mAlarmManager;
@Mock
MultiRateLimiter mToastRateLimiter;
BroadcastReceiver mPackageIntentReceiver;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@Mock
StatusBarManagerInternal mStatusBar;
private NotificationManagerService.WorkerHandler mWorkerHandler;
@Before
public void setUp() throws Exception {
// These should be the only difference in setup from NMSTest
Settings.Secure.putIntForUser(
getContext().getContentResolver(),
Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 1, USER_SYSTEM);
Settings.Global.putInt(getContext().getContentResolver(),
Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, 1);
// Shell permissions will override permissions of our app, so add all necessary permissions
// for this test here:
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
"android.permission.WRITE_DEVICE_CONFIG",
"android.permission.READ_DEVICE_CONFIG",
"android.permission.READ_CONTACTS");
MockitoAnnotations.initMocks(this);
when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.addService(StatusBarManagerInternal.class, mStatusBar);
LocalServices.removeServiceForTest(DeviceIdleInternal.class);
LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mAmi);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0});
doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger,
mNotificationInstanceIdSequence);
// Use this testable looper.
mTestableLooper = TestableLooper.get(this);
// MockPackageManager - default returns ApplicationInfo with matching calling UID
mContext.setMockPackageManager(mPackageManagerClient);
when(mPackageManager.getApplicationInfo(anyString(), anyLong(), anyInt()))
.thenAnswer((Answer<ApplicationInfo>) invocation -> {
Object[] args = invocation.getArguments();
return getApplicationInfo((String) args[0], mUid);
});
when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
.thenAnswer((Answer<ApplicationInfo>) invocation -> {
Object[] args = invocation.getArguments();
return getApplicationInfo((String) args[0], mUid);
});
when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid);
when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenAnswer(
(Answer<Boolean>) invocation -> {
Object[] args = invocation.getArguments();
return (int) args[1] == mUid;
});
final LightsManager mockLightsManager = mock(LightsManager.class);
when(mockLightsManager.getLight(anyInt())).thenReturn(mock(LogicalLight.class));
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG});
mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
// write to a test file; the system file isn't readable from tests
mFile = new File(mContext.getCacheDir(), "test.xml");
mFile.createNewFile();
final String preupgradeXml = "<notification-policy></notification-policy>";
mPolicyFile = new AtomicFile(mFile);
FileOutputStream fos = mPolicyFile.startWrite();
fos.write(preupgradeXml.getBytes());
mPolicyFile.finishWrite(fos);
// Setup managed services
when(mNlf.isTypeAllowed(anyInt())).thenReturn(true);
when(mNlf.isPackageAllowed(any())).thenReturn(true);
when(mNlf.isPackageAllowed(null)).thenReturn(true);
when(mListeners.getNotificationListenerFilter(any())).thenReturn(mNlf);
mListener = mListeners.new ManagedServiceInfo(
null, new ComponentName(PKG, "test_class"),
UserHandle.getUserId(mUid), true, null, 0, 123);
ComponentName defaultComponent = ComponentName.unflattenFromString("config/device");
ArraySet<ComponentName> components = new ArraySet<>();
components.add(defaultComponent);
when(mListeners.getDefaultComponents()).thenReturn(components);
when(mConditionProviders.getDefaultPackages())
.thenReturn(new ArraySet<>(Arrays.asList("config")));
when(mAssistants.getDefaultComponents()).thenReturn(components);
when(mAssistants.queryPackageForServices(
anyString(), anyInt(), anyInt())).thenReturn(components);
when(mListeners.checkServiceTokenLocked(null)).thenReturn(mListener);
ManagedServices.Config listenerConfig = new ManagedServices.Config();
listenerConfig.xmlTag = NotificationListeners.TAG_ENABLED_NOTIFICATION_LISTENERS;
when(mListeners.getConfig()).thenReturn(listenerConfig);
ManagedServices.Config assistantConfig = new ManagedServices.Config();
assistantConfig.xmlTag = NotificationAssistants.TAG_ENABLED_NOTIFICATION_ASSISTANTS;
when(mAssistants.getConfig()).thenReturn(assistantConfig);
ManagedServices.Config dndConfig = new ManagedServices.Config();
dndConfig.xmlTag = ConditionProviders.TAG_ENABLED_DND_APPS;
when(mConditionProviders.getConfig()).thenReturn(dndConfig);
when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);
// apps allowed as convos
mService.setStringArrayResourceValue(PKG_O);
mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm,
mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
mAppOpsManager, mock(IAppOpsService.class), mUm, mHistoryManager, mStatsManager,
mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper,
mock(UsageStatsManagerInternal.class), mock(TelecomManager.class));
// Return first true for RoleObserver main-thread check
when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
mService.setAudioManager(mAudioManager);
mShortcutHelper = mService.getShortcutHelper();
mShortcutHelper.setLauncherApps(mLauncherApps);
mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal);
mShortcutHelper.setUserManager(mUserManager);
// Capture PackageIntentReceiver
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
ArgumentCaptor<IntentFilter> intentFilterCaptor =
ArgumentCaptor.forClass(IntentFilter.class);
verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(),
any(), intentFilterCaptor.capture(), any(), any());
verify(mContext, atLeastOnce()).registerReceiver(broadcastReceiverCaptor.capture(),
intentFilterCaptor.capture());
List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues();
List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues();
for (int i = 0; i < intentFilters.size(); i++) {
final IntentFilter filter = intentFilters.get(i);
if (filter.hasAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED)
&& filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED)
&& filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) {
mPackageIntentReceiver = broadcastReceivers.get(i);
}
}
assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
ShortcutInfo info = mock(ShortcutInfo.class);
when(info.getPackage()).thenReturn(PKG);
when(info.getId()).thenReturn(VALID_CONVO_SHORTCUT_ID);
when(info.getUserId()).thenReturn(USER_SYSTEM);
when(info.isLongLived()).thenReturn(true);
when(info.isEnabled()).thenReturn(true);
shortcutInfos.add(info);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
anyString(), anyInt(), any())).thenReturn(true);
when(mUserManager.isUserUnlocked(any(UserHandle.class))).thenReturn(true);
// Set the testable bubble extractor
RankingHelper rankingHelper = mService.getRankingHelper();
BubbleExtractor extractor = rankingHelper.findExtractor(BubbleExtractor.class);
extractor.setActivityManager(mActivityManager);
// Tests call directly into the Binder.
mBinderService = mService.getBinderService();
mInternalService = mService.getInternalService();
mBinderService.createNotificationChannels(
PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
mBinderService.createNotificationChannels(
PKG_P, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
mBinderService.createNotificationChannels(
PKG_O, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
assertNotNull(mBinderService.getNotificationChannel(
PKG, mContext.getUserId(), PKG, TEST_CHANNEL_ID));
clearInvocations(mRankingHandler);
}
@After
public void tearDown() throws Exception {
if (mFile != null) mFile.delete();
try {
mService.onDestroy();
} catch (IllegalStateException | IllegalArgumentException e) {
// can throw if a broadcast receiver was never registered
}
InstrumentationRegistry.getInstrumentation()
.getUiAutomation().dropShellPermissionIdentity();
// Remove scheduled messages that would be processed when the test is already done, and
// could cause issues, for example, messages that remove/cancel shown toasts (this causes
// problematic interactions with mocks when they're no longer working as expected).
mWorkerHandler.removeCallbacksAndMessages(null);
}
private ApplicationInfo getApplicationInfo(String pkg, int uid) {
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = uid;
switch (pkg) {
case PKG_N_MR1:
applicationInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1;
break;
case PKG_O:
applicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
break;
case PKG_P:
applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
break;
default:
applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
break;
}
return applicationInfo;
}
public void waitForIdle() {
mTestableLooper.processAllMessages();
}
private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
return generateNotificationRecord(channel, null);
}
private NotificationRecord generateNotificationRecord(NotificationChannel channel,
Notification.TvExtender extender) {
if (channel == null) {
channel = mTestNotificationChannel;
}
Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.addAction(new Notification.Action.Builder(null, "test", null).build());
if (extender != null) {
nb.extend(extender);
}
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
return new NotificationRecord(mContext, sbn, channel);
}
private void enableInteractAcrossUsers() {
TestablePermissions perms = mContext.getTestablePermissions();
perms.setPermission(android.Manifest.permission.INTERACT_ACROSS_USERS, PERMISSION_GRANTED);
}
@Test
public void testAreNotificationsEnabledForPackage() throws Exception {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid);
verify(mPermissionHelper).hasPermission(mUid);
}
@Test
public void testAreNotificationsEnabledForPackage_crossUser() throws Exception {
try {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid + UserHandle.PER_USER_RANGE);
fail("Cannot call cross user without permission");
} catch (SecurityException e) {
// pass
}
verify(mPermissionHelper, never()).hasPermission(anyInt());
// cross user, with permission, no problem
enableInteractAcrossUsers();
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid + UserHandle.PER_USER_RANGE);
verify(mPermissionHelper).hasPermission(mUid + UserHandle.PER_USER_RANGE);
}
@Test
public void testAreNotificationsEnabledForPackage_viaInternalService() {
mInternalService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid);
verify(mPermissionHelper).hasPermission(mUid);
}
@Test
public void testGetPackageImportance() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
assertThat(mBinderService.getPackageImportance(mContext.getPackageName()))
.isEqualTo(IMPORTANCE_DEFAULT);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
assertThat(mBinderService.getPackageImportance(mContext.getPackageName()))
.isEqualTo(IMPORTANCE_NONE);
}
@Test
public void testEnqueueNotificationInternal_noChannel() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
NotificationRecord nr = generateNotificationRecord(
new NotificationChannel("did not create", "", IMPORTANCE_DEFAULT));
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
verify(mPermissionHelper).hasPermission(mUid);
verify(mPermissionHelper, never()).hasPermission(Process.SYSTEM_UID);
reset(mPermissionHelper);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
verify(mPermissionHelper).hasPermission(mUid);
assertThat(mService.mChannelToastsSent).contains(mUid);
}
@Test
public void testSetNotificationsEnabledForPackage_noChange() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, true);
verify(mPermissionHelper, never()).setNotificationPermission(
anyString(), anyInt(), anyBoolean(), anyBoolean());
}
@Test
public void testSetNotificationsEnabledForPackage() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, false);
verify(mPermissionHelper).setNotificationPermission(
mContext.getPackageName(), UserHandle.getUserId(mUid), false, true);
verify(mAppOpsManager, never()).setMode(anyInt(), anyInt(), anyString(), anyInt());
}
@Test
public void testUpdateAppNotifyCreatorBlock() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
Thread.sleep(500);
waitForIdle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
captor.getValue().getAction());
assertEquals(PKG, captor.getValue().getPackage());
assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
}
@Test
public void testUpdateAppNotifyCreatorUnblock() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
Thread.sleep(500);
waitForIdle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
captor.getValue().getAction());
assertEquals(PKG, captor.getValue().getPackage());
assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
}
@Test
public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
assertThat(mBinderService.getNotificationChannelsBypassingDnd(PKG, mUid).getList())
.isEmpty();
verify(mPreferencesHelper, never()).getImportance(anyString(), anyInt());
verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid);
}
@Test
public void testBlockedNotifications_blockedByUser() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_HIGH);
NotificationRecord r = generateNotificationRecord(channel);
mService.addEnqueuedNotification(r);
when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
NotificationManagerService.PostNotificationRunnable runnable =
mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
verify(mUsageStats).registerBlocked(any());
verify(mUsageStats, never()).registerPostedByApp(any());
}
@Test
public void testEnqueueNotification_appBlocked() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testEnqueueNotification_appBlocked", 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
verify(mWorkerHandler, never()).post(
any(NotificationManagerService.EnqueueNotificationRunnable.class));
}
@Test
public void testDefaultChannelDoesNotUpdateApp_postMigrationToPermissions() throws Exception {
final NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
PKG_N_MR1, ActivityManager.getCurrentUser(), PKG_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID);
defaultChannel.setImportance(IMPORTANCE_NONE);
mBinderService.updateNotificationChannelForPackage(PKG_N_MR1, mUid, defaultChannel);
verify(mPermissionHelper).setNotificationPermission(
PKG_N_MR1, ActivityManager.getCurrentUser(), false, true);
}
@Test
public void testPostNotification_appPermissionFixed() throws Exception {
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
when(mPermissionHelper.isPermissionFixed(PKG, 0)).thenReturn(true);
NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testPostNotification_appPermissionFixed", 0,
temp.getNotification(), 0);
waitForIdle();
assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(PKG);
assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
}
@Test
public void testSummaryNotification_appPermissionFixed() {
NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(temp);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true);
NotificationRecord r = mService.createAutoGroupSummary(
temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), false);
assertThat(r.isImportanceFixed()).isTrue();
}
@Test
public void testMediaNotificationsBypassBlock() throws Exception {
when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
.thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
Notification.Builder nb = new Notification.Builder(
mContext, mTestNotificationChannel.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.addAction(new Notification.Action.Builder(null, "test", null).build());
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
// normal blocked notifications - blocked
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
// just using the style - blocked
nb.setStyle(new Notification.MediaStyle());
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
// using the style, but incorrect type in session - blocked
nb.setStyle(new Notification.MediaStyle());
Bundle extras = new Bundle();
extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, new Intent());
nb.addExtras(extras);
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
// style + media session - bypasses block
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
}
@Test
public void testMediaNotificationsBypassBlock_atPost() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
Notification.Builder nb = new Notification.Builder(
mContext, mTestNotificationChannel.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.addAction(new Notification.Action.Builder(null, "test", null).build());
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
verify(mUsageStats).registerBlocked(any());
verify(mUsageStats, never()).registerPostedByApp(any());
// just using the style - blocked
mService.clearNotifications();
reset(mUsageStats);
nb.setStyle(new Notification.MediaStyle());
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addEnqueuedNotification(r);
runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
verify(mUsageStats).registerBlocked(any());
verify(mUsageStats, never()).registerPostedByApp(any());
// style + media session - bypasses block
mService.clearNotifications();
reset(mUsageStats);
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addEnqueuedNotification(r);
runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
verify(mUsageStats, never()).registerBlocked(any());
verify(mUsageStats).registerPostedByApp(any());
}
@Test
public void testGetAllUsersNotificationPermissions() {
// In this case, there are multiple users each with notification permissions (and also,
// for good measure, some without).
// make sure the collection returned contains info for all of them
final List<UserInfo> userInfos = new ArrayList<>();
userInfos.add(new UserInfo(0, "user0", 0));
userInfos.add(new UserInfo(1, "user1", 0));
userInfos.add(new UserInfo(2, "user2", 0));
when(mUm.getUsers()).thenReturn(userInfos);
// construct the permissions for each of them
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> permissions0 = new ArrayMap<>(),
permissions1 = new ArrayMap<>();
permissions0.put(new Pair<>(10, "package1"), new Pair<>(true, false));
permissions0.put(new Pair<>(20, "package2"), new Pair<>(false, true));
permissions1.put(new Pair<>(11, "package1"), new Pair<>(false, false));
permissions1.put(new Pair<>(21, "package2"), new Pair<>(true, true));
when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(permissions0);
when(mPermissionHelper.getNotificationPermissionValues(1)).thenReturn(permissions1);
when(mPermissionHelper.getNotificationPermissionValues(2)).thenReturn(new ArrayMap<>());
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> combinedPermissions =
mService.getAllUsersNotificationPermissions();
assertTrue(combinedPermissions.get(new Pair<>(10, "package1")).first);
assertFalse(combinedPermissions.get(new Pair<>(10, "package1")).second);
assertFalse(combinedPermissions.get(new Pair<>(20, "package2")).first);
assertTrue(combinedPermissions.get(new Pair<>(20, "package2")).second);
assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).first);
assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).second);
assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).first);
assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).second);
}
}

View File

@@ -1065,9 +1065,8 @@ public class NotificationRecordTest extends UiServiceTestCase {
}
@Test
public void testApplyImportanceAdjustmentsForNonOemDefaultAppLockedChannels() {
public void testApplyImportanceAdjustments() {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
channel.setImportanceLockedByOEM(false);
StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,

View File

@@ -88,51 +88,13 @@ public class PermissionHelperTest extends UiServiceTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true, false);
mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, false);
PackageInfo testPkgInfo = new PackageInfo();
testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.POST_NOTIFICATIONS };
when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt()))
.thenReturn(testPkgInfo);
}
// TODO (b/194833441): Remove when the migration is enabled
@Test
public void testMethodsThrowIfMigrationDisabled() throws IllegalAccessException,
InvocationTargetException {
PermissionHelper permHelper =
new PermissionHelper(mPmi, mPackageManager, mPermManager, false, false);
Method[] allMethods = PermissionHelper.class.getDeclaredMethods();
for (Method method : allMethods) {
if (Modifier.isPublic(method.getModifiers()) &&
!Objects.equals("isMigrationEnabled", method.getName())) {
Parameter[] params = method.getParameters();
List<Object> args = Lists.newArrayListWithCapacity(params.length);
for (int i = 0; i < params.length; i++) {
Type type = params[i].getParameterizedType();
if (type.getTypeName().equals("java.lang.String")) {
args.add("");
} else if (type.getTypeName().equals("boolean")){
args.add(false);
} else if (type.getTypeName().equals("int")) {
args.add(1);
} else if (type.getTypeName().equals(
"com.android.server.notification.PermissionHelper$PackagePermission")) {
args.add(null);
}
}
try {
method.invoke(permHelper, args.toArray());
fail("Method should have thrown because migration flag is disabled");
} catch (InvocationTargetException e) {
if (!(e.getTargetException() instanceof IllegalStateException)) {
throw e;
}
}
}
}
}
@Test
public void testHasPermission() throws Exception {
when(mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(anyInt()))
@@ -304,7 +266,7 @@ public class PermissionHelperTest extends UiServiceTestCase {
@Test
public void testSetNotificationPermission_pkgPerm_grantedByDefaultPermSet_allUserSet()
throws Exception {
mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true, true);
mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true);
when(mPmi.checkPermission(anyString(), anyString(), anyInt()))
.thenReturn(PERMISSION_DENIED);
when(mPermManager.getPermissionFlags(anyString(),

View File

@@ -168,7 +168,8 @@ public class RoleObserverTest extends UiServiceTestCase {
mock(StatsManager.class), mock(TelephonyManager.class),
mock(ActivityManagerInternal.class),
mock(MultiRateLimiter.class), mock(PermissionHelper.class),
mock(UsageStatsManagerInternal.class), mock (TelecomManager.class));
mock(UsageStatsManagerInternal.class), mock (TelecomManager.class),
mock(NotificationChannelLogger.class));
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;