From 5fe2eae6b9a2da7791b4686958425b6f0d89dc49 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Mon, 22 May 2017 08:45:27 -0400 Subject: [PATCH] Fix backup and restore of package blocks -Restore block status for apps that already exist E.g. apps on the system image - Stop depending on appops, it's duplicative and not backuped/restored Fixes: 38340377 Test: runtest systemui-notification, toast and notification cts tests, backup android, clear data on packages, restore android, verify that blocked apps stay blocked. Change-Id: I29a33d7db2701bd64a7b30411dcf77c0a2f321d1 --- .../NotificationManagerService.java | 86 +++-------------- .../server/notification/RankingHelper.java | 8 +- .../notification/RankingHelperTest.java | 95 +++++++++++++++---- 3 files changed, 91 insertions(+), 98 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index cb1742ea64ec6..4668156538c44 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -90,7 +90,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; -import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.ContentObserver; import android.media.AudioManager; @@ -114,7 +113,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; -import android.os.UserManager; import android.os.Vibrator; import android.os.VibrationEffect; import android.provider.Settings; @@ -473,19 +471,6 @@ public class NotificationManagerService extends SystemService { out.endDocument(); } - /** Use this when you actually want to post a notification or toast. - * - * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). - */ - private boolean noteNotificationOp(String pkg, int uid) { - if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) - != AppOpsManager.MODE_ALLOWED) { - Slog.v(TAG, "notifications are disabled by AppOps for " + pkg); - return false; - } - return true; - } - /** Use this to check if a package can post a notification or toast. */ private boolean checkNotificationOp(String pkg, int uid) { return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) @@ -1154,7 +1139,7 @@ public class NotificationManagerService extends SystemService { final File systemDir = new File(Environment.getDataDirectory(), "system"); mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); - syncBlockDb(); + loadPolicyFile(); // This is a ManagedServices object that keeps track of the listeners. mListeners = notificationListeners; @@ -1267,46 +1252,6 @@ public class NotificationManagerService extends SystemService { .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null); } - /** - * Make sure the XML config and the the AppOps system agree about blocks. - */ - private void syncBlockDb() { - loadPolicyFile(); - - // sync bans from ranker into app opps - Map packageBans = mRankingHelper.getPackageBans(); - for(Entry ban : packageBans.entrySet()) { - final int uid = ban.getKey(); - final String packageName = ban.getValue(); - setNotificationsEnabledForPackageImpl(packageName, uid, false); - } - - // sync bans from app opps into ranker - packageBans.clear(); - for (UserInfo user : UserManager.get(getContext()).getUsers()) { - final int userId = user.getUserHandle().getIdentifier(); - final PackageManager packageManager = getContext().getPackageManager(); - List packages = packageManager.getInstalledPackagesAsUser(0, userId); - final int packageCount = packages.size(); - for (int p = 0; p < packageCount; p++) { - final String packageName = packages.get(p).packageName; - try { - final int uid = packageManager.getPackageUidAsUser(packageName, userId); - if (!checkNotificationOp(packageName, uid)) { - packageBans.put(uid, packageName); - } - } catch (NameNotFoundException e) { - // forget you - } - } - } - for (Entry ban : packageBans.entrySet()) { - mRankingHelper.setImportance(ban.getValue(), ban.getKey(), IMPORTANCE_NONE); - } - - savePolicyFile(); - } - @Override public void onBootPhase(int phase) { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { @@ -1328,19 +1273,6 @@ public class NotificationManagerService extends SystemService { } } - void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { - Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); - - mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, - enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); - - // Now, cancel any outstanding notifications that are part of a just-disabled app - if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true, - UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null); - } - } - private void updateListenerHintsLocked() { final int hints = calculateHints(); if (hints == mListenerHints) return; @@ -1522,7 +1454,8 @@ public class NotificationManagerService extends SystemService { isPackageSuspendedForUser(pkg, Binder.getCallingUid()); if (ENABLE_BLOCKED_TOASTS && !isSystemToast && - (!noteNotificationOp(pkg, Binder.getCallingUid()) || isPackageSuspended)) { + (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid()) + || isPackageSuspended)) { Slog.e(TAG, "Suppressing toast from package " + pkg + (isPackageSuspended ? " due to package suspended by administrator." @@ -1643,8 +1576,12 @@ public class NotificationManagerService extends SystemService { public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { checkCallerIsSystem(); - setNotificationsEnabledForPackageImpl(pkg, uid, enabled); mRankingHelper.setEnabled(pkg, uid, enabled); + // Now, cancel any outstanding notifications that are part of a just-disabled app + if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { + cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true, + UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null); + } savePolicyFile(); } @@ -1662,8 +1599,8 @@ public class NotificationManagerService extends SystemService { @Override public boolean areNotificationsEnabledForPackage(String pkg, int uid) { checkCallerIsSystemOrSameApp(pkg); - return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) - == AppOpsManager.MODE_ALLOWED) && !isPackageSuspendedForUser(pkg, uid); + + return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE; } @Override @@ -3400,8 +3337,7 @@ public class NotificationManagerService extends SystemService { } final boolean isBlocked = r.getImportance() == NotificationManager.IMPORTANCE_NONE - || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE - || !noteNotificationOp(pkg, callingUid); + || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE; if (isBlocked) { Slog.e(TAG, "Suppressing notification from package by user request."); usageStats.registerBlocked(r); diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 55cb2f3ffba91..3dea7833964e3 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -21,8 +21,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.util.Preconditions; -import android.annotation.UserIdInt; -import android.app.ActivityManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; @@ -35,8 +33,6 @@ import android.content.pm.ParceledListSlice; import android.metrics.LogMaker; import android.os.Build; import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; import android.provider.Settings.Secure; import android.service.notification.NotificationListenerService.Ranking; import android.text.TextUtils; @@ -189,6 +185,10 @@ public class RankingHelper implements RankingConfig { safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY), safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY), safeBool(parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE)); + r.importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); + r.priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY); + r.visibility = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY); + r.showBadge = safeBool(parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE); final int innerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java index 06b5821b50d23..9f7c51552f14a 100644 --- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java @@ -27,6 +27,7 @@ import static junit.framework.Assert.fail; import org.json.JSONArray; import org.json.JSONObject; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -50,15 +51,11 @@ import android.net.Uri; import android.os.Build; import android.os.UserHandle; import android.provider.Settings.Secure; -import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; -import android.testing.TestableContext; -import android.testing.TestableSettingsProvider; import android.util.ArrayMap; -import android.util.Slog; import android.util.Xml; import java.io.BufferedInputStream; @@ -87,10 +84,10 @@ import static org.mockito.Mockito.when; public class RankingHelperTest extends NotificationTestCase { private static final String PKG = "com.android.server.notification"; private static final int UID = 0; - private static final UserHandle USER = UserHandle.getUserHandleForUid(UID); + private static final UserHandle USER = UserHandle.of(0); private static final String UPDATED_PKG = "updatedPkg"; - private static final int UID2 = 1111111; - private static final UserHandle USER2 = UserHandle.getUserHandleForUid(UID2); + private static final int UID2 = 1111; + private static final UserHandle USER2 = UserHandle.of(10); private static final String TEST_CHANNEL_ID = "test_channel_id"; @Mock NotificationUsageStats mUsageStats; @@ -199,24 +196,21 @@ public class RankingHelperTest extends NotificationTestCase { ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); - serializer.startTag(null, "ranking"); mHelper.writeXml(serializer, forBackup); - serializer.endTag(null, "ranking"); serializer.endDocument(); serializer.flush(); - for (String channelId : channelIds) { mHelper.permanentlyDeleteNotificationChannel(pkg, uid, channelId); } return baos; } - private void loadStreamXml(ByteArrayOutputStream stream) throws Exception { + private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(stream.toByteArray())), null); parser.nextTag(); - mHelper.readXml(parser, false); + mHelper.readXml(parser, forRestore); } private void compareChannels(NotificationChannel expected, NotificationChannel actual) { @@ -323,7 +317,7 @@ public class RankingHelperTest extends NotificationTestCase { channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID); mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID}); - loadStreamXml(baos); + loadStreamXml(baos, false); assertTrue(mHelper.canShowBadge(PKG, UID)); assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false)); @@ -353,6 +347,70 @@ public class RankingHelperTest extends NotificationTestCase { assertTrue(foundChannel2Group); } + @Test + public void testChannelXmlForBackup() throws Exception { + NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye"); + NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello"); + NotificationChannel channel1 = + new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); + NotificationChannel channel2 = + new NotificationChannel("id2", "name2", IMPORTANCE_LOW); + channel2.setDescription("descriptions for all"); + channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); + channel2.enableLights(true); + channel2.setBypassDnd(true); + channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET); + channel2.enableVibration(false); + channel2.setGroup(ncg.getId()); + channel2.setLightColor(Color.BLUE); + + mHelper.createNotificationChannelGroup(PKG, UID, ncg, true); + mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true); + mHelper.createNotificationChannel(PKG, UID, channel1, true); + mHelper.createNotificationChannel(PKG, UID, channel2, false); + mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true); + + mHelper.setShowBadge(PKG, UID, true); + + mHelper.setImportance(UPDATED_PKG, UID2, IMPORTANCE_NONE); + + ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(), + channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID); + mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG, UPDATED_PKG}, new int[]{UID, UID2}); + + mHelper.setShowBadge(UPDATED_PKG, UID2, true); + + loadStreamXml(baos, true); + + assertEquals(IMPORTANCE_NONE, mHelper.getImportance(UPDATED_PKG, UID2)); + assertTrue(mHelper.canShowBadge(PKG, UID)); + assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false)); + compareChannels(channel2, + mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false)); + + List actualGroups = + mHelper.getNotificationChannelGroups(PKG, UID, false).getList(); + boolean foundNcg = false; + for (NotificationChannelGroup actual : actualGroups) { + if (ncg.getId().equals(actual.getId())) { + foundNcg = true; + compareGroups(ncg, actual); + } else if (ncg2.getId().equals(actual.getId())) { + compareGroups(ncg2, actual); + } + } + assertTrue(foundNcg); + + boolean foundChannel2Group = false; + for (NotificationChannelGroup actual : actualGroups) { + if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) { + foundChannel2Group = true; + break; + } + } + assertTrue(foundChannel2Group); + } + @Test public void testChannelXml_backup() throws Exception { NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye"); @@ -397,7 +455,7 @@ public class RankingHelperTest extends NotificationTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false, NotificationChannel.DEFAULT_CHANNEL_ID); - loadStreamXml(baos); + loadStreamXml(baos, false); final NotificationChannel updated = mHelper.getNotificationChannel(PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false); @@ -417,7 +475,7 @@ public class RankingHelperTest extends NotificationTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false, NotificationChannel.DEFAULT_CHANNEL_ID); - loadStreamXml(baos); + loadStreamXml(baos, false); assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel( PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance()); @@ -465,7 +523,7 @@ public class RankingHelperTest extends NotificationTestCase { final ApplicationInfo upgraded = new ApplicationInfo(); upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1; when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded); - loadStreamXml(baos); + loadStreamXml(baos, false); // Default Channel should be gone. assertEquals(null, mHelper.getNotificationChannel(PKG, UID, @@ -483,7 +541,7 @@ public class RankingHelperTest extends NotificationTestCase { final ApplicationInfo upgraded = new ApplicationInfo(); upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1; when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded); - loadStreamXml(baos); + loadStreamXml(baos, false); // Default Channel should be gone. assertEquals(null, mHelper.getNotificationChannel(PKG, UID, @@ -497,7 +555,7 @@ public class RankingHelperTest extends NotificationTestCase { mHelper.createNotificationChannel(PKG, UID, new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true); - loadStreamXml(baos); + loadStreamXml(baos, false); // Should still have the newly created channel that wasn't in the xml. assertTrue(mHelper.getNotificationChannel(PKG, UID, "bananas", false) != null); @@ -1271,5 +1329,4 @@ public class RankingHelperTest extends NotificationTestCase { assertFalse(mHelper.badgingEnabled(USER)); assertTrue(mHelper.badgingEnabled(USER2)); } - }