Merge "Add retention policy to delete channels"

This commit is contained in:
Julia Reynolds
2021-01-19 14:54:56 +00:00
committed by Android (Google) Code Review
4 changed files with 149 additions and 8 deletions

View File

@@ -254,6 +254,7 @@ package android.app {
method public boolean isImportanceLockedByOEM();
method public void lockFields(int);
method public void setDeleted(boolean);
method public void setDeletedTimeMs(long);
method public void setDemoted(boolean);
method public void setFgServiceShown(boolean);
method public void setImportanceLockedByCriticalDeviceFunction(boolean);

View File

@@ -32,6 +32,7 @@ import android.os.Parcelable;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
@@ -111,6 +112,7 @@ public final class NotificationChannel implements Parcelable {
private static final String ATT_CONVERSATION_ID = "conv_id";
private static final String ATT_IMP_CONVERSATION = "imp_conv";
private static final String ATT_DEMOTE = "dem";
private static final String ATT_DELETED_TIME_MS = "del_time";
private static final String DELIMITER = ",";
/**
@@ -183,6 +185,7 @@ public final class NotificationChannel implements Parcelable {
NotificationManager.IMPORTANCE_UNSPECIFIED;
private static final boolean DEFAULT_DELETED = false;
private static final boolean DEFAULT_SHOW_BADGE = true;
private static final long DEFAULT_DELETION_TIME_MS = -1;
@UnsupportedAppUsage
private String mId;
@@ -214,6 +217,7 @@ public final class NotificationChannel implements Parcelable {
private String mConversationId = null;
private boolean mDemoted = false;
private boolean mImportantConvo = false;
private long mDeletedTime = DEFAULT_DELETION_TIME_MS;
/**
* Creates a notification channel.
@@ -282,6 +286,7 @@ public final class NotificationChannel implements Parcelable {
mConversationId = in.readString();
mDemoted = in.readBoolean();
mImportantConvo = in.readBoolean();
mDeletedTime = in.readLong();
}
@Override
@@ -341,6 +346,7 @@ public final class NotificationChannel implements Parcelable {
dest.writeString(mConversationId);
dest.writeBoolean(mDemoted);
dest.writeBoolean(mImportantConvo);
dest.writeLong(mDeletedTime);
}
/**
@@ -374,6 +380,14 @@ public final class NotificationChannel implements Parcelable {
mDeleted = deleted;
}
/**
* @hide
*/
@TestApi
public void setDeletedTimeMs(long time) {
mDeletedTime = time;
}
/**
* @hide
*/
@@ -763,6 +777,13 @@ public final class NotificationChannel implements Parcelable {
return mDeleted;
}
/**
* @hide
*/
public long getDeletedTimeMs() {
return mDeletedTime;
}
/**
* @hide
*/
@@ -906,6 +927,8 @@ public final class NotificationChannel implements Parcelable {
enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
setDeleted(safeBool(parser, ATT_DELETED, false));
setDeletedTimeMs(XmlUtils.readLongAttribute(
parser, ATT_DELETED_TIME_MS, DEFAULT_DELETION_TIME_MS));
setGroup(parser.getAttributeValue(null, ATT_GROUP));
lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
@@ -1024,6 +1047,9 @@ public final class NotificationChannel implements Parcelable {
if (isDeleted()) {
out.attributeBoolean(null, ATT_DELETED, isDeleted());
}
if (getDeletedTimeMs() >= 0) {
out.attributeLong(null, ATT_DELETED_TIME_MS, getDeletedTimeMs());
}
if (getGroup() != null) {
out.attribute(null, ATT_GROUP, getGroup());
}
@@ -1091,6 +1117,7 @@ public final class NotificationChannel implements Parcelable {
record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern()));
record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
record.put(ATT_DELETED, Boolean.toString(isDeleted()));
record.put(ATT_DELETED_TIME_MS, Long.toString(getDeletedTimeMs()));
record.put(ATT_GROUP, getGroup());
record.put(ATT_BLOCKABLE_SYSTEM, isBlockable());
record.put(ATT_ALLOW_BUBBLE, getAllowBubbles());
@@ -1182,6 +1209,7 @@ public final class NotificationChannel implements Parcelable {
&& mVibrationEnabled == that.mVibrationEnabled
&& mShowBadge == that.mShowBadge
&& isDeleted() == that.isDeleted()
&& getDeletedTimeMs() == that.getDeletedTimeMs()
&& isBlockable() == that.isBlockable()
&& mAllowBubbles == that.mAllowBubbles
&& Objects.equals(getId(), that.getId())
@@ -1205,8 +1233,8 @@ public final class NotificationChannel implements Parcelable {
int result = Objects.hash(getId(), getName(), mDesc, getImportance(), mBypassDnd,
getLockscreenVisibility(), getSound(), mLights, getLightColor(),
getUserLockedFields(),
isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
getAudioAttributes(), isBlockable(), mAllowBubbles,
isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(),
getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles,
mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
mParentId, mConversationId, mDemoted, mImportantConvo);
result = 31 * result + Arrays.hashCode(mVibration);
@@ -1247,6 +1275,7 @@ public final class NotificationChannel implements Parcelable {
+ ", mVibrationEnabled=" + mVibrationEnabled
+ ", mShowBadge=" + mShowBadge
+ ", mDeleted=" + mDeleted
+ ", mDeletedTimeMs=" + mDeletedTime
+ ", mGroup='" + mGroup + '\''
+ ", mAudioAttributes=" + mAudioAttributes
+ ", mBlockableSystem=" + mBlockableSystem

View File

@@ -50,6 +50,7 @@ import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerService;
import android.service.notification.RankingHelperProto;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
@@ -101,6 +102,7 @@ public class PreferencesHelper implements RankingConfig {
private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000;
private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000;
private static final int NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT = 1000;
private static final int NOTIFICATION_CHANNEL_DELETION_RETENTION_DAYS = 30;
@VisibleForTesting
static final String TAG_RANKING = "ranking";
@@ -324,12 +326,8 @@ public class PreferencesHelper implements RankingConfig {
channel.setImportanceLockedByOEM(true);
}
}
boolean isInvalidShortcutChannel =
channel.getConversationId() != null &&
channel.getConversationId().contains(
PLACEHOLDER_CONVERSATION_ID);
if (mAllowInvalidShortcuts || (!mAllowInvalidShortcuts
&& !isInvalidShortcutChannel)) {
if (isShortcutOk(channel) && isDeletionOk(channel)) {
r.channels.put(id, channel);
}
}
@@ -369,6 +367,26 @@ public class PreferencesHelper implements RankingConfig {
throw new IllegalStateException("Failed to reach END_DOCUMENT");
}
private boolean isShortcutOk(NotificationChannel channel) {
boolean isInvalidShortcutChannel =
channel.getConversationId() != null &&
channel.getConversationId().contains(
PLACEHOLDER_CONVERSATION_ID);
return mAllowInvalidShortcuts || (!mAllowInvalidShortcuts && !isInvalidShortcutChannel);
}
private boolean isDeletionOk(NotificationChannel nc) {
if (!nc.isDeleted()) {
return true;
}
long boundary = System.currentTimeMillis() - (
DateUtils.DAY_IN_MILLIS * NOTIFICATION_CHANNEL_DELETION_RETENTION_DAYS);
if (nc.getDeletedTimeMs() <= boundary) {
return false;
}
return true;
}
private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) {
final String key = packagePreferencesKey(pkg, uid);
return mPackagePreferences.get(key);
@@ -828,6 +846,7 @@ public class PreferencesHelper implements RankingConfig {
if (existing.isDeleted()) {
// The existing channel was deleted - undelete it.
existing.setDeleted(false);
existing.setDeletedTimeMs(-1);
needsPolicyFileChange = true;
wasUndeleted = true;
@@ -1119,6 +1138,7 @@ public class PreferencesHelper implements RankingConfig {
private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
if (!channel.isDeleted()) {
channel.setDeleted(true);
channel.setDeletedTimeMs(System.currentTimeMillis());
LogMaker lm = getChannelLog(channel, pkg);
lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
MetricsLogger.action(lm);
@@ -1479,6 +1499,7 @@ public class PreferencesHelper implements RankingConfig {
if (nc.getConversationId() != null
&& conversationIds.contains(nc.getConversationId())) {
nc.setDeleted(true);
nc.setDeletedTimeMs(System.currentTimeMillis());
LogMaker lm = getChannelLog(nc, pkg);
lm.setType(
com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);

View File

@@ -95,6 +95,7 @@ import android.provider.Settings.Secure;
import android.service.notification.ConversationChannelWrapper;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
@@ -3292,6 +3293,95 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
}
@Test
public void testDeleted_noTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
mAppOpsManager, mStatsEventBuilderFactory);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\" deleted=\"true\"/>"
+ "</package>"
+ "</ranking>";
TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
null);
parser.nextTag();
mHelper.readXml(parser, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
}
@Test
public void testDeleted_recentTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
mAppOpsManager, mStatsEventBuilderFactory);
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false);
mHelper.deleteNotificationChannel(PKG_P, UID_P, "id");
NotificationChannel nc1 = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
assertTrue(DateUtils.isToday(nc1.getDeletedTimeMs()));
assertTrue(nc1.isDeleted());
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_P, UID_P, false,
UserHandle.USER_SYSTEM, "id", NotificationChannel.DEFAULT_CHANNEL_ID);
TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
null);
parser.nextTag();
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
mAppOpsManager, mStatsEventBuilderFactory);
mHelper.readXml(parser, true, UserHandle.USER_SYSTEM);
NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
assertTrue(DateUtils.isToday(nc.getDeletedTimeMs()));
assertTrue(nc.isDeleted());
}
@Test
public void testUnDelete_time() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
mAppOpsManager, mStatsEventBuilderFactory);
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false);
mHelper.deleteNotificationChannel(PKG_P, UID_P, "id");
NotificationChannel nc1 = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
assertTrue(DateUtils.isToday(nc1.getDeletedTimeMs()));
assertTrue(nc1.isDeleted());
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false);
nc1 = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
assertEquals(-1, nc1.getDeletedTimeMs());
assertFalse(nc1.isDeleted());
}
@Test
public void testDeleted_longTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
mAppOpsManager, mStatsEventBuilderFactory);
long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\" deleted=\"true\" del_time=\""
+ time + "\"/>"
+ "</package>"
+ "</ranking>";
TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
null);
parser.nextTag();
mHelper.readXml(parser, false, UserHandle.USER_ALL);
NotificationChannel nc = mHelper.getNotificationChannel(PKG_O, UID_O, "id", true);
assertNull(nc);
}
@Test
public void testGetConversations_all() {
String convoId = "convo";