Merge "Add retention policy to delete channels"
This commit is contained in:
committed by
Android (Google) Code Review
commit
10395bf408
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user