summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Matías Hernández <matiashe@google.com> 2025-02-14 07:32:19 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-02-14 07:32:19 -0800
commit387b32ce9d1d80a58c58f50f1993e5de085bee06 (patch)
tree71f5c0e3261716cff14d328c88fbbf8b2247a74d
parentc26379f212e327cbf9ee93b3f72fb08be07a1b82 (diff)
parentdfce623b20a061b9ff6426ae918d4ab4622ce041 (diff)
Merge "Remove long unused (and non-user-modified) implicit ZenRules" into main
-rw-r--r--core/java/android/app/notification.aconfig7
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java110
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java54
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java34
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java332
6 files changed, 455 insertions, 85 deletions
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index bb4f556532f7..8e6b88c66408 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -83,6 +83,13 @@ flag {
}
flag {
+ name: "modes_cleanup_implicit"
+ namespace: "systemui"
+ description: "Deletes implicit modes if never customized and not used for some time. Depends on MODES_UI"
+ bug: "394087495"
+}
+
+flag {
name: "api_tvextender"
is_exported: true
namespace: "systemui"
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 4011574da879..6f94c1b2d274 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -309,6 +309,7 @@ public class ZenModeConfig implements Parcelable {
private static final String RULE_ATT_DISABLED_ORIGIN = "disabledOrigin";
private static final String RULE_ATT_LEGACY_SUPPRESSED_EFFECTS = "legacySuppressedEffects";
private static final String RULE_ATT_CONDITION_OVERRIDE = "conditionOverride";
+ private static final String RULE_ATT_LAST_ACTIVATION = "lastActivation";
private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale";
private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY =
@@ -1187,11 +1188,7 @@ public class ZenModeConfig implements Parcelable {
rt.zenPolicyUserModifiedFields = safeInt(parser, POLICY_USER_MODIFIED_FIELDS, 0);
rt.zenDeviceEffectsUserModifiedFields = safeInt(parser,
DEVICE_EFFECT_USER_MODIFIED_FIELDS, 0);
- Long deletionInstant = tryParseLong(
- parser.getAttributeValue(null, RULE_ATT_DELETION_INSTANT), null);
- if (deletionInstant != null) {
- rt.deletionInstant = Instant.ofEpochMilli(deletionInstant);
- }
+ rt.deletionInstant = safeInstant(parser, RULE_ATT_DELETION_INSTANT, null);
if (Flags.modesUi()) {
rt.disabledOrigin = safeInt(parser, RULE_ATT_DISABLED_ORIGIN,
ORIGIN_UNKNOWN);
@@ -1199,6 +1196,9 @@ public class ZenModeConfig implements Parcelable {
RULE_ATT_LEGACY_SUPPRESSED_EFFECTS, 0);
rt.conditionOverride = safeInt(parser, RULE_ATT_CONDITION_OVERRIDE,
ZenRule.OVERRIDE_NONE);
+ if (Flags.modesCleanupImplicit()) {
+ rt.lastActivation = safeInstant(parser, RULE_ATT_LAST_ACTIVATION, null);
+ }
}
return rt;
@@ -1249,10 +1249,7 @@ public class ZenModeConfig implements Parcelable {
out.attributeInt(null, POLICY_USER_MODIFIED_FIELDS, rule.zenPolicyUserModifiedFields);
out.attributeInt(null, DEVICE_EFFECT_USER_MODIFIED_FIELDS,
rule.zenDeviceEffectsUserModifiedFields);
- if (rule.deletionInstant != null) {
- out.attributeLong(null, RULE_ATT_DELETION_INSTANT,
- rule.deletionInstant.toEpochMilli());
- }
+ writeXmlAttributeInstant(out, RULE_ATT_DELETION_INSTANT, rule.deletionInstant);
if (Flags.modesUi()) {
out.attributeInt(null, RULE_ATT_DISABLED_ORIGIN, rule.disabledOrigin);
out.attributeInt(null, RULE_ATT_LEGACY_SUPPRESSED_EFFECTS,
@@ -1260,6 +1257,16 @@ public class ZenModeConfig implements Parcelable {
if (rule.conditionOverride == ZenRule.OVERRIDE_ACTIVATE && !forBackup) {
out.attributeInt(null, RULE_ATT_CONDITION_OVERRIDE, rule.conditionOverride);
}
+ if (Flags.modesCleanupImplicit()) {
+ writeXmlAttributeInstant(out, RULE_ATT_LAST_ACTIVATION, rule.lastActivation);
+ }
+ }
+ }
+
+ private static void writeXmlAttributeInstant(TypedXmlSerializer out, String att,
+ @Nullable Instant instant) throws IOException {
+ if (instant != null) {
+ out.attributeLong(null, att, instant.toEpochMilli());
}
}
@@ -1600,6 +1607,19 @@ public class ZenModeConfig implements Parcelable {
return values;
}
+ @Nullable
+ private static Instant safeInstant(TypedXmlPullParser parser, String att,
+ @Nullable Instant defValue) {
+ final String strValue = parser.getAttributeValue(null, att);
+ if (!TextUtils.isEmpty(strValue)) {
+ Long longValue = tryParseLong(strValue, null);
+ if (longValue != null) {
+ return Instant.ofEpochMilli(longValue);
+ }
+ }
+ return defValue;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -2598,6 +2618,18 @@ public class ZenModeConfig implements Parcelable {
@ConditionOverride
int conditionOverride = OVERRIDE_NONE;
+ /**
+ * Last time at which the rule was activated (for any reason, including overrides).
+ * If {@code null}, the rule has never been activated since its creation.
+ *
+ * <p>Note that this was previously untracked, so it will also be {@code null} for rules
+ * created before we started tracking and never activated since -- make sure to account for
+ * it, for example by falling back to {@link #creationTime} in logic involving this field.
+ */
+ @Nullable
+ @FlaggedApi(Flags.FLAG_MODES_CLEANUP_IMPLICIT)
+ public Instant lastActivation;
+
public ZenRule() { }
public ZenRule(Parcel source) {
@@ -2635,24 +2667,29 @@ public class ZenModeConfig implements Parcelable {
disabledOrigin = source.readInt();
legacySuppressedEffects = source.readInt();
conditionOverride = source.readInt();
+ if (Flags.modesCleanupImplicit()) {
+ if (source.readInt() == 1) {
+ lastActivation = Instant.ofEpochMilli(source.readLong());
+ }
+ }
}
}
/**
- * Whether this ZenRule can be updated by an app. In general, rules that have been
- * customized by the user cannot be further updated by an app, with some exceptions:
+ * Whether this ZenRule has been customized by the user in any way.
+
+ * <p>In general, rules that have been customized by the user cannot be further updated by
+ * an app, with some exceptions:
* <ul>
* <li>Non user-configurable fields, like type, icon, configurationActivity, etc.
* <li>Name, if the name was not specifically modified by the user (to support language
* switches).
* </ul>
*/
- public boolean canBeUpdatedByApp() {
- // The rule is considered updateable if its bitmask has no user modifications, and
- // the bitmasks of the policy and device effects have no modification.
- return userModifiedFields == 0
- && zenPolicyUserModifiedFields == 0
- && zenDeviceEffectsUserModifiedFields == 0;
+ public boolean isUserModified() {
+ return userModifiedFields != 0
+ || zenPolicyUserModifiedFields != 0
+ || zenDeviceEffectsUserModifiedFields != 0;
}
@Override
@@ -2708,6 +2745,14 @@ public class ZenModeConfig implements Parcelable {
dest.writeInt(disabledOrigin);
dest.writeInt(legacySuppressedEffects);
dest.writeInt(conditionOverride);
+ if (Flags.modesCleanupImplicit()) {
+ if (lastActivation != null) {
+ dest.writeInt(1);
+ dest.writeLong(lastActivation.toEpochMilli());
+ } else {
+ dest.writeInt(0);
+ }
+ }
}
}
@@ -2760,6 +2805,9 @@ public class ZenModeConfig implements Parcelable {
if (Flags.modesUi()) {
sb.append(",disabledOrigin=").append(disabledOrigin);
sb.append(",legacySuppressedEffects=").append(legacySuppressedEffects);
+ if (Flags.modesCleanupImplicit()) {
+ sb.append(",lastActivation=").append(lastActivation);
+ }
}
return sb.append(']').toString();
@@ -2838,6 +2886,10 @@ public class ZenModeConfig implements Parcelable {
&& other.disabledOrigin == disabledOrigin
&& other.legacySuppressedEffects == legacySuppressedEffects
&& other.conditionOverride == conditionOverride;
+ if (Flags.modesCleanupImplicit()) {
+ finalEquals = finalEquals
+ && Objects.equals(other.lastActivation, lastActivation);
+ }
}
return finalEquals;
@@ -2846,13 +2898,23 @@ public class ZenModeConfig implements Parcelable {
@Override
public int hashCode() {
if (Flags.modesUi()) {
- return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
- component, configurationActivity, pkg, id, enabler, zenPolicy,
- zenDeviceEffects, allowManualInvocation, iconResName,
- triggerDescription, type, userModifiedFields,
- zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
- deletionInstant, disabledOrigin, legacySuppressedEffects,
- conditionOverride);
+ if (Flags.modesCleanupImplicit()) {
+ return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
+ component, configurationActivity, pkg, id, enabler, zenPolicy,
+ zenDeviceEffects, allowManualInvocation, iconResName,
+ triggerDescription, type, userModifiedFields,
+ zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
+ deletionInstant, disabledOrigin, legacySuppressedEffects,
+ conditionOverride, lastActivation);
+ } else {
+ return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
+ component, configurationActivity, pkg, id, enabler, zenPolicy,
+ zenDeviceEffects, allowManualInvocation, iconResName,
+ triggerDescription, type, userModifiedFields,
+ zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
+ deletionInstant, disabledOrigin, legacySuppressedEffects,
+ conditionOverride);
+ }
} else {
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
component, configurationActivity, pkg, id, enabler, zenPolicy,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f7a4d3d9132c..889df512dd60 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -157,6 +157,12 @@ public class ZenModeHelper {
static final int RULE_LIMIT_PER_PACKAGE = 100;
private static final Duration DELETED_RULE_KEPT_FOR = Duration.ofDays(30);
+ /**
+ * Amount of time since last activation after which implicit rules that have never been
+ * customized by the user are automatically cleaned up.
+ */
+ private static final Duration IMPLICIT_RULE_KEPT_FOR = Duration.ofDays(30);
+
private static final int MAX_ICON_RESOURCE_NAME_LENGTH = 1000;
/**
@@ -534,7 +540,7 @@ public class ZenModeHelper {
ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
if (sleepingRule != null
&& !sleepingRule.enabled
- && sleepingRule.canBeUpdatedByApp() /* meaning it's not user-customized */) {
+ && !sleepingRule.isUserModified()) {
config.automaticRules.remove(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
}
}
@@ -864,7 +870,7 @@ public class ZenModeHelper {
// We don't try to preserve system-owned rules because their conditionIds (used as
// deletedRuleKey) are not stable. This is almost moot anyway because an app cannot
// delete a system-owned rule.
- if (origin == ORIGIN_APP && !ruleToRemove.canBeUpdatedByApp()
+ if (origin == ORIGIN_APP && ruleToRemove.isUserModified()
&& !PACKAGE_ANDROID.equals(ruleToRemove.pkg)) {
String deletedKey = ZenModeConfig.deletedRuleKey(ruleToRemove);
if (deletedKey != null) {
@@ -1282,7 +1288,7 @@ public class ZenModeHelper {
// * the request comes from an origin that can always update values, like the user, or
// * the rule has not yet been user modified, and thus can be updated by the app.
boolean updateValues = isNew || doesOriginAlwaysUpdateValues(origin)
- || rule.canBeUpdatedByApp();
+ || !rule.isUserModified();
// For all other values, if updates are not allowed, we discard the update.
if (!updateValues) {
@@ -1914,6 +1920,7 @@ public class ZenModeHelper {
* <ul>
* <li>Rule instances whose owner is not installed.
* <li>Deleted rules that were deleted more than 30 days ago.
+ * <li>Implicit rules that haven't been used in 30 days (and have not been customized).
* </ul>
*/
private void cleanUpZenRules() {
@@ -1932,6 +1939,10 @@ public class ZenModeHelper {
}
}
+ if (Flags.modesUi() && Flags.modesCleanupImplicit()) {
+ deleteUnusedImplicitRules(newConfig.automaticRules);
+ }
+
if (!newConfig.equals(mConfig)) {
setConfigLocked(newConfig, null, ORIGIN_SYSTEM,
"cleanUpZenRules", Process.SYSTEM_UID);
@@ -1957,6 +1968,29 @@ public class ZenModeHelper {
}
}
+ private void deleteUnusedImplicitRules(ArrayMap<String, ZenRule> ruleList) {
+ if (ruleList == null) {
+ return;
+ }
+ Instant deleteIfUnusedSince = mClock.instant().minus(IMPLICIT_RULE_KEPT_FOR);
+
+ for (int i = ruleList.size() - 1; i >= 0; i--) {
+ ZenRule rule = ruleList.valueAt(i);
+ if (isImplicitRuleId(rule.id) && !rule.isUserModified()) {
+ if (rule.lastActivation == null) {
+ // This rule existed before we started tracking activation time. It *might* be
+ // in use. Set lastActivation=now so it has some time (IMPLICIT_RULE_KEPT_FOR)
+ // before being removed if truly unused.
+ rule.lastActivation = mClock.instant();
+ }
+
+ if (rule.lastActivation.isBefore(deleteIfUnusedSince)) {
+ ruleList.removeAt(i);
+ }
+ }
+ }
+ }
+
/**
* @return a copy of the zen mode configuration
*/
@@ -2091,6 +2125,20 @@ public class ZenModeHelper {
}
}
+ // Update last activation for rules that are being activated.
+ if (Flags.modesUi() && Flags.modesCleanupImplicit()) {
+ Instant now = mClock.instant();
+ if (!mConfig.isManualActive() && config.isManualActive()) {
+ config.manualRule.lastActivation = now;
+ }
+ for (ZenRule rule : config.automaticRules.values()) {
+ ZenRule previousRule = mConfig.automaticRules.get(rule.id);
+ if (rule.isActive() && (previousRule == null || !previousRule.isActive())) {
+ rule.lastActivation = now;
+ }
+ }
+ }
+
mConfig = config;
dispatchOnConfigChanged();
updateAndApplyConsolidatedPolicyAndDeviceEffects(origin, reason);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 4c1544f14667..67efb9e76692 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -488,33 +488,33 @@ public class ZenModeConfigTest extends UiServiceTestCase {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.zenPolicy = null;
rule.zenDeviceEffects = null;
- assertThat(rule.canBeUpdatedByApp()).isTrue();
+ assertThat(rule.isUserModified()).isFalse();
rule.userModifiedFields = 1;
- assertThat(rule.canBeUpdatedByApp()).isFalse();
+ assertThat(rule.isUserModified()).isTrue();
}
@Test
public void testCanBeUpdatedByApp_policyModified() throws Exception {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.zenPolicy = new ZenPolicy();
- assertThat(rule.canBeUpdatedByApp()).isTrue();
+ assertThat(rule.isUserModified()).isFalse();
rule.zenPolicyUserModifiedFields = 1;
- assertThat(rule.canBeUpdatedByApp()).isFalse();
+ assertThat(rule.isUserModified()).isTrue();
}
@Test
public void testCanBeUpdatedByApp_deviceEffectsModified() throws Exception {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.zenDeviceEffects = new ZenDeviceEffects.Builder().build();
- assertThat(rule.canBeUpdatedByApp()).isTrue();
+ assertThat(rule.isUserModified()).isFalse();
rule.zenDeviceEffectsUserModifiedFields = 1;
- assertThat(rule.canBeUpdatedByApp()).isFalse();
+ assertThat(rule.isUserModified()).isTrue();
}
@Test
@@ -563,6 +563,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
if (Flags.modesUi()) {
rule.disabledOrigin = ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+ if (Flags.modesCleanupImplicit()) {
+ rule.lastActivation = Instant.ofEpochMilli(456);
+ }
}
config.automaticRules.put(rule.id, rule);
@@ -600,6 +603,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.deletionInstant, ruleActual.deletionInstant);
if (Flags.modesUi()) {
assertEquals(rule.disabledOrigin, ruleActual.disabledOrigin);
+ if (Flags.modesCleanupImplicit()) {
+ assertEquals(rule.lastActivation, ruleActual.lastActivation);
+ }
}
if (Flags.backupRestoreLogging()) {
verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_RULES, 2);
@@ -633,6 +639,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
if (Flags.modesUi()) {
rule.disabledOrigin = ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+ if (Flags.modesCleanupImplicit()) {
+ rule.lastActivation = Instant.ofEpochMilli(789);
+ }
}
Parcel parcel = Parcel.obtain();
@@ -664,6 +673,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.deletionInstant, parceled.deletionInstant);
if (Flags.modesUi()) {
assertEquals(rule.disabledOrigin, parceled.disabledOrigin);
+ if (Flags.modesCleanupImplicit()) {
+ assertEquals(rule.lastActivation, parceled.lastActivation);
+ }
}
assertEquals(rule, parceled);
@@ -746,6 +758,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
if (Flags.modesUi()) {
rule.disabledOrigin = ZenModeConfig.ORIGIN_APP;
+ if (Flags.modesCleanupImplicit()) {
+ rule.lastActivation = Instant.ofEpochMilli(123);
+ }
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -781,6 +796,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.deletionInstant, fromXml.deletionInstant);
if (Flags.modesUi()) {
assertEquals(rule.disabledOrigin, fromXml.disabledOrigin);
+ if (Flags.modesCleanupImplicit()) {
+ assertEquals(rule.lastActivation, fromXml.lastActivation);
+ }
}
}
@@ -908,7 +926,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.userModifiedFields |= AutomaticZenRule.FIELD_NAME;
assertThat(rule.userModifiedFields).isEqualTo(1);
- assertThat(rule.canBeUpdatedByApp()).isFalse();
+ assertThat(rule.isUserModified()).isTrue();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeRuleXml(rule, baos);
@@ -916,7 +934,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
assertThat(fromXml.userModifiedFields).isEqualTo(rule.userModifiedFields);
- assertThat(fromXml.canBeUpdatedByApp()).isFalse();
+ assertThat(fromXml.isUserModified()).isTrue();
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index 8a5f80cb3e49..6d0bf8b322fd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -475,7 +475,8 @@ public class ZenModeDiffTest extends UiServiceTestCase {
// "Metadata" fields are never compared.
Set<String> exemptFields = new LinkedHashSet<>(
Set.of("userModifiedFields", "zenPolicyUserModifiedFields",
- "zenDeviceEffectsUserModifiedFields", "deletionInstant", "disabledOrigin"));
+ "zenDeviceEffectsUserModifiedFields", "deletionInstant", "disabledOrigin",
+ "lastActivation"));
// Flagged fields are only compared if their flag is on.
if (Flags.modesUi()) {
exemptFields.add(RuleDiff.FIELD_SNOOZING); // Obsolete.
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 4d2f105e27b3..0ab11e0cbe3d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -23,6 +23,7 @@ import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
import static android.app.AutomaticZenRule.TYPE_THEATER;
import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
import static android.app.Flags.FLAG_BACKUP_RESTORE_LOGGING;
+import static android.app.Flags.FLAG_MODES_CLEANUP_IMPLICIT;
import static android.app.Flags.FLAG_MODES_MULTIUSER;
import static android.app.Flags.FLAG_MODES_UI;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
@@ -124,7 +125,10 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
+import static java.time.temporal.ChronoUnit.DAYS;
+
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.AlarmManager;
@@ -219,7 +223,6 @@ import java.io.Reader;
import java.io.StringWriter;
import java.time.Instant;
import java.time.ZoneOffset;
-import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
@@ -2233,8 +2236,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mConfig.automaticRules.put(implicitRuleBeforeModesUi.id,
implicitRuleBeforeModesUi);
// Plus one other normal rule.
- ZenRule anotherRule = newZenRule("other_pkg", Instant.now(), null);
- anotherRule.id = "other_rule";
+ ZenRule anotherRule = newZenRule("other_rule", "other_pkg", Instant.now());
anotherRule.iconResName = "other_icon";
anotherRule.type = TYPE_IMMERSIVE;
mZenModeHelper.mConfig.automaticRules.put(anotherRule.id, anotherRule);
@@ -2271,8 +2273,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
implicitRuleWithModesUi);
// Plus one other normal rule.
- ZenRule anotherRule = newZenRule("other_pkg", Instant.now(), null);
- anotherRule.id = "other_rule";
+ ZenRule anotherRule = newZenRule("other_rule", "other_pkg", Instant.now());
anotherRule.iconResName = "other_icon";
anotherRule.type = TYPE_IMMERSIVE;
mZenModeHelper.mConfig.automaticRules.put(anotherRule.id, anotherRule);
@@ -4611,7 +4612,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
- assertThat(storedRule.canBeUpdatedByApp()).isTrue();
+ assertThat(storedRule.isUserModified()).isFalse();
}
@Test
@@ -4719,7 +4720,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
STATE_DISALLOW);
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
- assertThat(storedRule.canBeUpdatedByApp()).isFalse();
+ assertThat(storedRule.isUserModified()).isTrue();
assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(
ZenPolicy.FIELD_ALLOW_CHANNELS
| ZenPolicy.FIELD_PRIORITY_CATEGORY_REMINDERS
@@ -4761,7 +4762,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
- assertThat(storedRule.canBeUpdatedByApp()).isFalse();
+ assertThat(storedRule.isUserModified()).isTrue();
assertThat(storedRule.zenDeviceEffectsUserModifiedFields).isEqualTo(
ZenDeviceEffects.FIELD_GRAYSCALE);
}
@@ -5713,8 +5714,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Start with deleted rules from 2 different packages.
Instant now = Instant.ofEpochMilli(1701796461000L);
- ZenRule pkg1Rule = newZenRule("pkg1", now.minus(1, ChronoUnit.DAYS), now);
- ZenRule pkg2Rule = newZenRule("pkg2", now.minus(2, ChronoUnit.DAYS), now);
+ ZenRule pkg1Rule = newDeletedZenRule("1", "pkg1", now.minus(1, DAYS), now);
+ ZenRule pkg2Rule = newDeletedZenRule("2", "pkg2", now.minus(2, DAYS), now);
mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg1Rule), pkg1Rule);
mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg2Rule), pkg2Rule);
@@ -5832,9 +5833,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testRuleCleanup() throws Exception {
Instant now = Instant.ofEpochMilli(1701796461000L);
- Instant yesterday = now.minus(1, ChronoUnit.DAYS);
- Instant aWeekAgo = now.minus(7, ChronoUnit.DAYS);
- Instant twoMonthsAgo = now.minus(60, ChronoUnit.DAYS);
+ Instant yesterday = now.minus(1, DAYS);
+ Instant aWeekAgo = now.minus(7, DAYS);
+ Instant twoMonthsAgo = now.minus(60, DAYS);
mTestClock.setNowMillis(now.toEpochMilli());
when(mPackageManager.getPackageInfo(eq("good_pkg"), anyInt()))
@@ -5847,24 +5848,28 @@ public class ZenModeHelperTest extends UiServiceTestCase {
config.user = 42;
mZenModeHelper.mConfigs.put(42, config);
// okay rules (not deleted, package exists, with a range of creation dates).
- config.automaticRules.put("ar1", newZenRule("good_pkg", now, null));
- config.automaticRules.put("ar2", newZenRule("good_pkg", yesterday, null));
- config.automaticRules.put("ar3", newZenRule("good_pkg", twoMonthsAgo, null));
+ config.automaticRules.put("ar1", newZenRule("ar1", "good_pkg", now));
+ config.automaticRules.put("ar2", newZenRule("ar2", "good_pkg", yesterday));
+ config.automaticRules.put("ar3", newZenRule("ar3", "good_pkg", twoMonthsAgo));
// newish rules for a missing package
- config.automaticRules.put("ar4", newZenRule("bad_pkg", yesterday, null));
+ config.automaticRules.put("ar4", newZenRule("ar4", "bad_pkg", yesterday));
// oldish rules belonging to a missing package
- config.automaticRules.put("ar5", newZenRule("bad_pkg", aWeekAgo, null));
+ config.automaticRules.put("ar5", newZenRule("ar5", "bad_pkg", aWeekAgo));
// rules deleted recently
- config.deletedRules.put("del1", newZenRule("good_pkg", twoMonthsAgo, yesterday));
- config.deletedRules.put("del2", newZenRule("good_pkg", twoMonthsAgo, aWeekAgo));
+ config.deletedRules.put("del1",
+ newDeletedZenRule("del1", "good_pkg", twoMonthsAgo, yesterday));
+ config.deletedRules.put("del2",
+ newDeletedZenRule("del2", "good_pkg", twoMonthsAgo, aWeekAgo));
// rules deleted a long time ago
- config.deletedRules.put("del3", newZenRule("good_pkg", twoMonthsAgo, twoMonthsAgo));
+ config.deletedRules.put("del3",
+ newDeletedZenRule("del3", "good_pkg", twoMonthsAgo, twoMonthsAgo));
// rules for a missing package, created recently and deleted recently
- config.deletedRules.put("del4", newZenRule("bad_pkg", yesterday, now));
+ config.deletedRules.put("del4", newDeletedZenRule("del4", "bad_pkg", yesterday, now));
// rules for a missing package, created a long time ago and deleted recently
- config.deletedRules.put("del5", newZenRule("bad_pkg", twoMonthsAgo, now));
+ config.deletedRules.put("del5", newDeletedZenRule("del5", "bad_pkg", twoMonthsAgo, now));
// rules for a missing package, created a long time ago and deleted a long time ago
- config.deletedRules.put("del6", newZenRule("bad_pkg", twoMonthsAgo, twoMonthsAgo));
+ config.deletedRules.put("del6",
+ newDeletedZenRule("del6", "bad_pkg", twoMonthsAgo, twoMonthsAgo));
mZenModeHelper.onUserSwitched(42); // copies config and cleans it up.
@@ -5874,14 +5879,115 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.containsExactly("del1", "del2", "del4");
}
- private static ZenRule newZenRule(String pkg, Instant createdAt, @Nullable Instant deletedAt) {
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void testRuleCleanup_removesNotRecentlyUsedNotModifiedImplicitRules() throws Exception {
+ Instant now = Instant.ofEpochMilli(1701796461000L);
+ Instant yesterday = now.minus(1, DAYS);
+ Instant aWeekAgo = now.minus(7, DAYS);
+ Instant twoMonthsAgo = now.minus(60, DAYS);
+ Instant aYearAgo = now.minus(365, DAYS);
+ mTestClock.setNowMillis(now.toEpochMilli());
+ when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(new PackageInfo());
+
+ // Set up a config to be loaded, containing a bunch of implicit rules
+ ZenModeConfig config = new ZenModeConfig();
+ config.user = 42;
+ mZenModeHelper.mConfigs.put(42, config);
+ // used recently
+ ZenRule usedRecently1 = newImplicitZenRule("pkg1", aYearAgo, yesterday);
+ ZenRule usedRecently2 = newImplicitZenRule("pkg2", aYearAgo, aWeekAgo);
+ config.automaticRules.put(usedRecently1.id, usedRecently1);
+ config.automaticRules.put(usedRecently2.id, usedRecently2);
+ // not used in a long time
+ ZenRule longUnused = newImplicitZenRule("pkg3", aYearAgo, twoMonthsAgo);
+ config.automaticRules.put(longUnused.id, longUnused);
+ // created a long time ago, before lastActivation tracking
+ ZenRule oldAndLastUsageUnknown = newImplicitZenRule("pkg4", twoMonthsAgo, null);
+ config.automaticRules.put(oldAndLastUsageUnknown.id, oldAndLastUsageUnknown);
+ // created a short time ago, before lastActivation tracking
+ ZenRule newAndLastUsageUnknown = newImplicitZenRule("pkg5", aWeekAgo, null);
+ config.automaticRules.put(newAndLastUsageUnknown.id, newAndLastUsageUnknown);
+ // not used in a long time, but was customized by user
+ ZenRule longUnusedButCustomized = newImplicitZenRule("pkg6", aYearAgo, twoMonthsAgo);
+ longUnusedButCustomized.zenPolicyUserModifiedFields = ZenPolicy.FIELD_CONVERSATIONS;
+ config.automaticRules.put(longUnusedButCustomized.id, longUnusedButCustomized);
+ // created a long time ago, before lastActivation tracking, and was customized by user
+ ZenRule oldAndLastUsageUnknownAndCustomized = newImplicitZenRule("pkg7", twoMonthsAgo,
+ null);
+ oldAndLastUsageUnknownAndCustomized.userModifiedFields = AutomaticZenRule.FIELD_ICON;
+ config.automaticRules.put(oldAndLastUsageUnknownAndCustomized.id,
+ oldAndLastUsageUnknownAndCustomized);
+
+ mZenModeHelper.onUserSwitched(42); // copies config and cleans it up.
+
+ // The recently used OR modified OR last-used-unknown rules stay.
+ assertThat(mZenModeHelper.mConfig.automaticRules.values())
+ .comparingElementsUsing(IGNORE_METADATA)
+ .containsExactly(usedRecently1, usedRecently2, oldAndLastUsageUnknown,
+ newAndLastUsageUnknown, longUnusedButCustomized,
+ oldAndLastUsageUnknownAndCustomized);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void testRuleCleanup_assignsLastActivationToImplicitRules() throws Exception {
+ Instant now = Instant.ofEpochMilli(1701796461000L);
+ Instant aWeekAgo = now.minus(7, DAYS);
+ Instant aYearAgo = now.minus(365, DAYS);
+ mTestClock.setNowMillis(now.toEpochMilli());
+ when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(new PackageInfo());
+
+ // Set up a config to be loaded, containing implicit rules.
+ ZenModeConfig config = new ZenModeConfig();
+ config.user = 42;
+ mZenModeHelper.mConfigs.put(42, config);
+ // with last activation known
+ ZenRule usedRecently = newImplicitZenRule("pkg1", aYearAgo, aWeekAgo);
+ config.automaticRules.put(usedRecently.id, usedRecently);
+ // created a long time ago, with last activation unknown
+ ZenRule oldAndLastUsageUnknown = newImplicitZenRule("pkg4", aYearAgo, null);
+ config.automaticRules.put(oldAndLastUsageUnknown.id, oldAndLastUsageUnknown);
+ // created a short time ago, with last activation unknown
+ ZenRule newAndLastUsageUnknown = newImplicitZenRule("pkg5", aWeekAgo, null);
+ config.automaticRules.put(newAndLastUsageUnknown.id, newAndLastUsageUnknown);
+
+ mZenModeHelper.onUserSwitched(42); // copies config and cleans it up.
+
+ // All rules stayed.
+ usedRecently = getZenRule(usedRecently.id);
+ oldAndLastUsageUnknown = getZenRule(oldAndLastUsageUnknown.id);
+ newAndLastUsageUnknown = getZenRule(newAndLastUsageUnknown.id);
+
+ // The rules with an unknown last usage have been assigned a placeholder one.
+ assertThat(usedRecently.lastActivation).isEqualTo(aWeekAgo);
+ assertThat(oldAndLastUsageUnknown.lastActivation).isEqualTo(now);
+ assertThat(newAndLastUsageUnknown.lastActivation).isEqualTo(now);
+ }
+
+ private static ZenRule newDeletedZenRule(String id, String pkg, Instant createdAt,
+ @NonNull Instant deletedAt) {
+ ZenRule rule = newZenRule(id, pkg, createdAt);
+ rule.deletionInstant = deletedAt;
+ return rule;
+ }
+
+ private static ZenRule newImplicitZenRule(String pkg, @NonNull Instant createdAt,
+ @Nullable Instant lastActivatedAt) {
+ ZenRule implicitRule = newZenRule(implicitRuleId(pkg), pkg, createdAt);
+ implicitRule.lastActivation = lastActivatedAt;
+ return implicitRule;
+ }
+
+ private static ZenRule newZenRule(String id, String pkg, Instant createdAt) {
ZenRule rule = new ZenRule();
+ rule.id = id;
rule.pkg = pkg;
rule.creationTime = createdAt.toEpochMilli();
rule.enabled = true;
- rule.deletionInstant = deletedAt;
+ rule.deletionInstant = null;
// Plus stuff so that isValidAutomaticRule() passes
- rule.name = "A rule from " + pkg + " created on " + createdAt;
+ rule.name = "Rule " + id;
rule.conditionId = Uri.parse(rule.name);
return rule;
}
@@ -5919,11 +6025,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void getAutomaticZenRuleState_notOwnedRule_returnsStateUnknown() {
// Assume existence of a system-owned rule that is currently ACTIVE.
- ZenRule systemRule = newZenRule("android", Instant.now(), null);
+ ZenRule systemRule = newZenRule("systemRule", "android", Instant.now());
systemRule.zenMode = ZEN_MODE_ALARMS;
systemRule.condition = new Condition(systemRule.conditionId, "on", Condition.STATE_TRUE);
ZenModeConfig config = mZenModeHelper.mConfig.copy();
- config.automaticRules.put("systemRule", systemRule);
+ config.automaticRules.put(systemRule.id, systemRule);
mZenModeHelper.setConfig(config, null, ORIGIN_INIT, "", SYSTEM_UID);
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
@@ -5935,11 +6041,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
public void setAutomaticZenRuleState_idForNotOwnedRule_ignored() {
// Assume existence of an other-package-owned rule that is currently ACTIVE.
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
- ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
+ ZenRule otherRule = newZenRule("otherRule", "another.package", Instant.now());
otherRule.zenMode = ZEN_MODE_ALARMS;
otherRule.condition = new Condition(otherRule.conditionId, "on", Condition.STATE_TRUE);
ZenModeConfig config = mZenModeHelper.mConfig.copy();
- config.automaticRules.put("otherRule", otherRule);
+ config.automaticRules.put(otherRule.id, otherRule);
mZenModeHelper.setConfig(config, null, ORIGIN_INIT, "", SYSTEM_UID);
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
@@ -5955,11 +6061,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
public void setAutomaticZenRuleStateFromConditionProvider_conditionForNotOwnedRule_ignored() {
// Assume existence of an other-package-owned rule that is currently ACTIVE.
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
- ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
+ ZenRule otherRule = newZenRule("otherRule", "another.package", Instant.now());
otherRule.zenMode = ZEN_MODE_ALARMS;
otherRule.condition = new Condition(otherRule.conditionId, "on", Condition.STATE_TRUE);
ZenModeConfig config = mZenModeHelper.mConfig.copy();
- config.automaticRules.put("otherRule", otherRule);
+ config.automaticRules.put(otherRule.id, otherRule);
mZenModeHelper.setConfig(config, null, ORIGIN_INIT, "", SYSTEM_UID);
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
@@ -7255,6 +7361,125 @@ public class ZenModeHelperTest extends UiServiceTestCase {
"config: setAzrStateFromCps: cond/cond (ORIGIN_APP) from uid " + CUSTOM_PKG_UID);
}
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void setAutomaticZenRuleState_updatesLastActivation() {
+ String ruleOne = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg,
+ new AutomaticZenRule.Builder("rule", CONDITION_ID)
+ .setConfigurationActivity(new ComponentName(mPkg, "cls"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build(),
+ ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ String ruleTwo = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg,
+ new AutomaticZenRule.Builder("unrelated", Uri.parse("other.condition"))
+ .setConfigurationActivity(new ComponentName(mPkg, "cls"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build(),
+ ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleOne).lastActivation).isNull();
+ assertThat(getZenRule(ruleTwo).lastActivation).isNull();
+
+ Instant firstActivation = Instant.ofEpochMilli(100);
+ mTestClock.setNow(firstActivation);
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleOne, CONDITION_TRUE,
+ ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleOne).lastActivation).isEqualTo(firstActivation);
+ assertThat(getZenRule(ruleTwo).lastActivation).isNull();
+
+ mTestClock.setNow(Instant.ofEpochMilli(300));
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleOne, CONDITION_FALSE,
+ ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleOne).lastActivation).isEqualTo(firstActivation);
+ assertThat(getZenRule(ruleTwo).lastActivation).isNull();
+
+ Instant secondActivation = Instant.ofEpochMilli(500);
+ mTestClock.setNow(secondActivation);
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleOne, CONDITION_TRUE,
+ ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleOne).lastActivation).isEqualTo(secondActivation);
+ assertThat(getZenRule(ruleTwo).lastActivation).isNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void setManualZenMode_updatesLastActivation() {
+ assertThat(mZenModeHelper.mConfig.manualRule.lastActivation).isNull();
+ Instant instant = Instant.ofEpochMilli(100);
+ mTestClock.setNow(instant);
+
+ mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_ALARMS, null,
+ ORIGIN_USER_IN_SYSTEMUI, "reason", "systemui", SYSTEM_UID);
+
+ assertThat(mZenModeHelper.mConfig.manualRule.lastActivation).isEqualTo(instant);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void applyGlobalZenModeAsImplicitZenRule_updatesLastActivation() {
+ Instant instant = Instant.ofEpochMilli(100);
+ mTestClock.setNow(instant);
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+ CUSTOM_PKG_UID, ZEN_MODE_ALARMS);
+
+ ZenRule implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME));
+ assertThat(implicitRule.lastActivation).isEqualTo(instant);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void setAutomaticZenRuleState_notChangingActiveState_doesNotUpdateLastActivation() {
+ String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg,
+ new AutomaticZenRule.Builder("rule", CONDITION_ID)
+ .setConfigurationActivity(new ComponentName(mPkg, "cls"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build(),
+ ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleId).lastActivation).isNull();
+
+ // Manual activation comes first
+ Instant firstActivation = Instant.ofEpochMilli(100);
+ mTestClock.setNow(firstActivation);
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+
+ assertThat(getZenRule(ruleId).lastActivation).isEqualTo(firstActivation);
+
+ // Now the app says the rule should be active (assume it's on a schedule, and the app
+ // doesn't listen to broadcasts so it doesn't know an override was present). This doesn't
+ // change the activation state.
+ mTestClock.setNow(Instant.ofEpochMilli(300));
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+ ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleId).lastActivation).isEqualTo(firstActivation);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void addOrUpdateRule_doesNotUpdateLastActivation() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder("rule", CONDITION_ID)
+ .setConfigurationActivity(new ComponentName(mPkg, "cls"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build();
+
+ String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, azr,
+ ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleId).lastActivation).isNull();
+
+ mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ new AutomaticZenRule.Builder(azr).setName("New name").build(), ORIGIN_APP, "reason",
+ CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleId).lastActivation).isNull();
+ }
+
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
@@ -7272,22 +7497,27 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
private static final Correspondence<ZenRule, ZenRule> IGNORE_METADATA =
- Correspondence.transforming(zr -> {
- Parcel p = Parcel.obtain();
- try {
- zr.writeToParcel(p, 0);
- p.setDataPosition(0);
- ZenRule copy = new ZenRule(p);
- copy.creationTime = 0;
- copy.userModifiedFields = 0;
- copy.zenPolicyUserModifiedFields = 0;
- copy.zenDeviceEffectsUserModifiedFields = 0;
- return copy;
- } finally {
- p.recycle();
- }
- },
- "Ignoring timestamp and userModifiedFields");
+ Correspondence.transforming(
+ ZenModeHelperTest::cloneWithoutMetadata,
+ ZenModeHelperTest::cloneWithoutMetadata,
+ "Ignoring timestamps and userModifiedFields");
+
+ private static ZenRule cloneWithoutMetadata(ZenRule rule) {
+ Parcel p = Parcel.obtain();
+ try {
+ rule.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ ZenRule copy = new ZenRule(p);
+ copy.creationTime = 0;
+ copy.userModifiedFields = 0;
+ copy.zenPolicyUserModifiedFields = 0;
+ copy.zenDeviceEffectsUserModifiedFields = 0;
+ copy.lastActivation = null;
+ return copy;
+ } finally {
+ p.recycle();
+ }
+ }
private ZenRule expectedImplicitRule(String ownerPkg, int zenMode, ZenPolicy policy,
@Nullable Boolean conditionActive) {
@@ -7693,6 +7923,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
return mNowMillis;
}
+ private void setNow(Instant instant) {
+ mNowMillis = instant.toEpochMilli();
+ }
+
private void setNowMillis(long millis) {
mNowMillis = millis;
}