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
This commit is contained in:
@@ -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<Integer, String> packageBans = mRankingHelper.getPackageBans();
|
||||
for(Entry<Integer, String> 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<PackageInfo> 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<Integer, String> 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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<NotificationChannelGroup> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user