Synchronize on subscription list.

Bug: 27676283
Change-Id: I0382369895ba54abab7e43e28b72495db9f84f94
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index b13fec1..5bc9b1c 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -44,6 +44,8 @@
 import com.android.server.notification.NotificationManagerService.DumpFilter;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Built-in zen condition provider for calendar event-based conditions.
@@ -96,10 +98,12 @@
         pw.print("      mRegistered="); pw.println(mRegistered);
         pw.print("      mBootComplete="); pw.println(mBootComplete);
         dumpUpcomingTime(pw, "mNextAlarmTime", mNextAlarmTime, System.currentTimeMillis());
-        pw.println("      mSubscriptions=");
-        for (Uri conditionId : mSubscriptions) {
-            pw.print("        ");
-            pw.println(conditionId);
+        synchronized (mSubscriptions) {
+            pw.println("      mSubscriptions=");
+            for (Uri conditionId : mSubscriptions) {
+                pw.print("        ");
+                pw.println(conditionId);
+            }
         }
         pw.println("      mTrackers=");
         for (int i = 0; i < mTrackers.size(); i++) {
@@ -142,19 +146,23 @@
     public void onSubscribe(Uri conditionId) {
         if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
         if (!ZenModeConfig.isValidEventConditionId(conditionId)) {
-            notifyCondition(conditionId, Condition.STATE_FALSE, "badCondition");
+            notifyCondition(createCondition(conditionId, Condition.STATE_FALSE));
             return;
         }
-        if (mSubscriptions.add(conditionId)) {
-            evaluateSubscriptions();
+        synchronized (mSubscriptions) {
+            if (mSubscriptions.add(conditionId)) {
+                evaluateSubscriptions();
+            }
         }
     }
 
     @Override
     public void onUnsubscribe(Uri conditionId) {
         if (DEBUG) Slog.d(TAG, "onUnsubscribe " + conditionId);
-        if (mSubscriptions.remove(conditionId)) {
-            evaluateSubscriptions();
+        synchronized (mSubscriptions) {
+            if (mSubscriptions.remove(conditionId)) {
+                evaluateSubscriptions();
+            }
         }
     }
 
@@ -198,51 +206,61 @@
             return;
         }
         final long now = System.currentTimeMillis();
-        for (int i = 0; i < mTrackers.size(); i++) {
-            mTrackers.valueAt(i).setCallback(mSubscriptions.isEmpty() ? null : mTrackerCallback);
-        }
-        setRegistered(!mSubscriptions.isEmpty());
-        long reevaluateAt = 0;
-        for (Uri conditionId : mSubscriptions) {
-            final EventInfo event = ZenModeConfig.tryParseEventConditionId(conditionId);
-            if (event == null) {
-                notifyCondition(conditionId, Condition.STATE_FALSE, "badConditionId");
-                continue;
+        List<Condition> conditionsToNotify = new ArrayList<>();
+        synchronized (mSubscriptions) {
+            for (int i = 0; i < mTrackers.size(); i++) {
+                mTrackers.valueAt(i).setCallback(
+                        mSubscriptions.isEmpty() ? null : mTrackerCallback);
             }
-            CheckEventResult result = null;
-            if (event.calendar == null) { // any calendar
-                // event could exist on any tracker
-                for (int i = 0; i < mTrackers.size(); i++) {
-                    final CalendarTracker tracker = mTrackers.valueAt(i);
-                    final CheckEventResult r = tracker.checkEvent(event, now);
-                    if (result == null) {
-                        result = r;
-                    } else {
-                        result.inEvent |= r.inEvent;
-                        result.recheckAt = Math.min(result.recheckAt, r.recheckAt);
-                    }
-                }
-            } else {
-                // event should exist on one tracker
-                final int userId = EventInfo.resolveUserId(event.userId);
-                final CalendarTracker tracker = mTrackers.get(userId);
-                if (tracker == null) {
-                    Slog.w(TAG, "No calendar tracker found for user " + userId);
-                    notifyCondition(conditionId, Condition.STATE_FALSE, "badUserId");
+            setRegistered(!mSubscriptions.isEmpty());
+            long reevaluateAt = 0;
+            for (Uri conditionId : mSubscriptions) {
+                final EventInfo event = ZenModeConfig.tryParseEventConditionId(conditionId);
+                if (event == null) {
+                    conditionsToNotify.add(createCondition(conditionId, Condition.STATE_FALSE));
                     continue;
                 }
-                result = tracker.checkEvent(event, now);
+                CheckEventResult result = null;
+                if (event.calendar == null) { // any calendar
+                    // event could exist on any tracker
+                    for (int i = 0; i < mTrackers.size(); i++) {
+                        final CalendarTracker tracker = mTrackers.valueAt(i);
+                        final CheckEventResult r = tracker.checkEvent(event, now);
+                        if (result == null) {
+                            result = r;
+                        } else {
+                            result.inEvent |= r.inEvent;
+                            result.recheckAt = Math.min(result.recheckAt, r.recheckAt);
+                        }
+                    }
+                } else {
+                    // event should exist on one tracker
+                    final int userId = EventInfo.resolveUserId(event.userId);
+                    final CalendarTracker tracker = mTrackers.get(userId);
+                    if (tracker == null) {
+                        Slog.w(TAG, "No calendar tracker found for user " + userId);
+                        conditionsToNotify.add(createCondition(conditionId, Condition.STATE_FALSE));
+                        continue;
+                    }
+                    result = tracker.checkEvent(event, now);
+                }
+                if (result.recheckAt != 0
+                        && (reevaluateAt == 0 || result.recheckAt < reevaluateAt)) {
+                    reevaluateAt = result.recheckAt;
+                }
+                if (!result.inEvent) {
+                    conditionsToNotify.add(createCondition(conditionId, Condition.STATE_FALSE));
+                    continue;
+                }
+                conditionsToNotify.add(createCondition(conditionId, Condition.STATE_TRUE));
             }
-            if (result.recheckAt != 0 && (reevaluateAt == 0 || result.recheckAt < reevaluateAt)) {
-                reevaluateAt = result.recheckAt;
-            }
-            if (!result.inEvent) {
-                notifyCondition(conditionId, Condition.STATE_FALSE, "!inEventNow");
-                continue;
-            }
-            notifyCondition(conditionId, Condition.STATE_TRUE, "inEventNow");
+            rescheduleAlarm(now, reevaluateAt);
         }
-        rescheduleAlarm(now, reevaluateAt);
+        for (Condition condition : conditionsToNotify) {
+            if (condition != null) {
+                notifyCondition(condition);
+            }
+        }
         if (DEBUG) Slog.d(TAG, "evaluateSubscriptions took " + (System.currentTimeMillis() - now));
     }
 
@@ -266,12 +284,6 @@
         alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
     }
 
-    private void notifyCondition(Uri conditionId, int state, String reason) {
-        if (DEBUG) Slog.d(TAG, "notifyCondition " + conditionId + " "
-                + Condition.stateToString(state) + " reason=" + reason);
-        notifyCondition(createCondition(conditionId, state));
-    }
-
     private Condition createCondition(Uri id, int state) {
         final String summary = NOT_SHOWN;
         final String line1 = NOT_SHOWN;