diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 69960b04518a0..e90a3ba543513 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -44,6 +44,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.Objects; @@ -896,9 +897,13 @@ public class ZenModeConfig implements Parcelable { ", endHour=" + endHour + ", endMinute=" + endMinute + ", exitAtAlarm=" + exitAtAlarm + - ", nextAlarm=" + nextAlarm + + ", nextAlarm=" + ts(nextAlarm) + '}'; } + + protected static String ts(long time) { + return new Date(time) + " (" + time + ")"; + } } // ==== Built-in system condition: event ==== diff --git a/services/core/java/com/android/server/notification/ScheduleCalendar.java b/services/core/java/com/android/server/notification/ScheduleCalendar.java index 22ca7024dd5b8..9e8b2e34520b2 100644 --- a/services/core/java/com/android/server/notification/ScheduleCalendar.java +++ b/services/core/java/com/android/server/notification/ScheduleCalendar.java @@ -82,15 +82,13 @@ public class ScheduleCalendar { if (end <= start) { end = addDays(end, 1); } - boolean isInSchedule = - isInSchedule(-1, time, start, end) || isInSchedule(0, time, start, end); - if (isInSchedule && mSchedule.exitAtAlarm + return isInSchedule(-1, time, start, end) || isInSchedule(0, time, start, end); + } + + public boolean shouldExitForAlarm(long time) { + return mSchedule.exitAtAlarm && mSchedule.nextAlarm != 0 - && time >= mSchedule.nextAlarm) { - return false; - } else { - return isInSchedule; - } + && time >= mSchedule.nextAlarm; } private boolean isInSchedule(int daysOffset, long time, long start, long end) { diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java index 81975443bf50d..32d03ced1be82 100644 --- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java +++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java @@ -25,11 +25,15 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; +import android.os.Binder; +import android.provider.Settings; import android.service.notification.Condition; import android.service.notification.IConditionProvider; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.ScheduleInfo; +import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -53,9 +57,13 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { private static final String ACTION_EVALUATE = SIMPLE_NAME + ".EVALUATE"; private static final int REQUEST_CODE_EVALUATE = 1; private static final String EXTRA_TIME = "time"; + private static final String SEPARATOR = ";"; + private static final String SCP_SETTING = "snoozed_schedule_condition_provider"; + private final Context mContext = this; private final ArrayMap mSubscriptions = new ArrayMap<>(); + private ArraySet mSnoozed = new ArraySet<>(); private AlarmManager mAlarmManager; private boolean mConnected; @@ -90,6 +98,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { pw.print(" "); pw.println(mSubscriptions.get(conditionId).toString()); } + pw.println(" snoozed due to alarm: " + TextUtils.join(SEPARATOR, mSnoozed)); dumpUpcomingTime(pw, "mNextAlarmTime", mNextAlarmTime, now); } @@ -97,6 +106,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { public void onConnected() { if (DEBUG) Slog.d(TAG, "onConnected"); mConnected = true; + readSnoozed(); } @Override @@ -126,6 +136,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { public void onUnsubscribe(Uri conditionId) { if (DEBUG) Slog.d(TAG, "onUnsubscribe " + conditionId); mSubscriptions.remove(conditionId); + removeSnoozed(conditionId); evaluateSubscriptions(); } @@ -150,10 +161,16 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { for (Uri conditionId : mSubscriptions.keySet()) { final ScheduleCalendar cal = mSubscriptions.get(conditionId); if (cal != null && cal.isInSchedule(now)) { - notifyCondition(conditionId, Condition.STATE_TRUE, "meetsSchedule"); + if (conditionSnoozed(conditionId) || cal.shouldExitForAlarm(now)) { + notifyCondition(conditionId, Condition.STATE_FALSE, "alarmCanceled"); + addSnoozed(conditionId); + } else { + notifyCondition(conditionId, Condition.STATE_TRUE, "meetsSchedule"); + } cal.maybeSetNextAlarm(now, nextUserAlarmTime); } else { notifyCondition(conditionId, Condition.STATE_FALSE, "!meetsSchedule"); + removeSnoozed(conditionId); if (nextUserAlarmTime == 0) { cal.maybeSetNextAlarm(now, nextUserAlarmTime); } @@ -194,7 +211,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { return info != null ? info.getTriggerTime() : 0; } - private static boolean meetsSchedule(ScheduleCalendar cal, long time) { + private boolean meetsSchedule(ScheduleCalendar cal, long time) { return cal != null && cal.isInSchedule(time); } @@ -237,6 +254,62 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { return new Condition(id, summary, line1, line2, 0, state, Condition.FLAG_RELEVANT_ALWAYS); } + private boolean conditionSnoozed(Uri conditionId) { + synchronized (mSnoozed) { + return mSnoozed.contains(conditionId); + } + } + + private void addSnoozed(Uri conditionId) { + synchronized (mSnoozed) { + mSnoozed.add(conditionId); + saveSnoozedLocked(); + } + } + + private void removeSnoozed(Uri conditionId) { + synchronized (mSnoozed) { + mSnoozed.remove(conditionId); + saveSnoozedLocked(); + } + } + + public void saveSnoozedLocked() { + final String setting = TextUtils.join(SEPARATOR, mSnoozed); + final int currentUser = ActivityManager.getCurrentUser(); + Settings.Secure.putStringForUser(mContext.getContentResolver(), + SCP_SETTING, + setting, + currentUser); + } + + public void readSnoozed() { + synchronized (mSnoozed) { + long identity = Binder.clearCallingIdentity(); + try { + final String setting = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + SCP_SETTING, + ActivityManager.getCurrentUser()); + if (setting != null) { + final String[] tokens = setting.split(SEPARATOR); + for (int i = 0; i < tokens.length; i++) { + String token = tokens[i]; + if (token != null) { + token = token.trim(); + } + if (TextUtils.isEmpty(token)) { + continue; + } + mSnoozed.add(Uri.parse(token)); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) {