Zen: Calendar auto-rule tracks primary calendar for corp profile.

 - Track the calendar provider for the managed profile user if found.
 - Add userId to the shared data structure to disambig calendar ids.
 - Delay rule update a bit to guard against chatty updates.
 - Fix logging in calendar rule.

Bug: 21155107
Change-Id: Id2303fcc39b1fa7417b1844b7869d773ef92434c
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index dea6325..46cc47b 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -23,7 +23,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.service.notification.Condition;
 import android.service.notification.IConditionProvider;
 import android.service.notification.ZenModeConfig;
@@ -31,6 +36,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.server.notification.CalendarTracker.CheckEventResult;
 import com.android.server.notification.NotificationManagerService.DumpFilter;
@@ -51,17 +57,21 @@
     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 long CHANGE_DELAY = 2 * 1000;  // coalesce chatty calendar changes
 
     private final Context mContext = this;
     private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>();
-    private final CalendarTracker mTracker = new CalendarTracker(mContext);
+    private final SparseArray<CalendarTracker> mTrackers = new SparseArray<>();
+    private final Handler mWorker;
 
     private boolean mConnected;
     private boolean mRegistered;
     private boolean mBootComplete;  // don't hammer the calendar provider until boot completes.
+    private long mNextAlarmTime;
 
-    public EventConditionProvider() {
+    public EventConditionProvider(Looper worker) {
         if (DEBUG) Slog.d(TAG, "new " + SIMPLE_NAME + "()");
+        mWorker = new Handler(worker);
     }
 
     @Override
@@ -80,13 +90,17 @@
         pw.print("      mConnected="); pw.println(mConnected);
         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);
         }
-        pw.println("      mTracker=");
-        mTracker.dump("        ", pw);
+        pw.println("      mTrackers=");
+        for (int i = 0; i < mTrackers.size(); i++) {
+            pw.print("        user="); pw.println(mTrackers.keyAt(i));
+            mTrackers.valueAt(i).dump("          ", pw);
+        }
     }
 
     @Override
@@ -94,7 +108,16 @@
         if (DEBUG) Slog.d(TAG, "onBootComplete");
         if (mBootComplete) return;
         mBootComplete = true;
-        evaluateSubscriptions();
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                reloadTrackers();
+            }
+        }, filter);
+        reloadTrackers();
     }
 
     @Override
@@ -146,14 +169,39 @@
         return (IConditionProvider) onBind(null);
     }
 
+    private void reloadTrackers() {
+        if (DEBUG) Slog.d(TAG, "reloadTrackers");
+        for (int i = 0; i < mTrackers.size(); i++) {
+            mTrackers.valueAt(i).setCallback(null);
+        }
+        mTrackers.clear();
+        for (UserHandle user : UserManager.get(mContext).getUserProfiles()) {
+            final Context context = user.isOwner() ? mContext : getContextForUser(mContext, user);
+            if (context == null) {
+                Slog.w(TAG, "Unable to create context for user " + user.getIdentifier());
+                continue;
+            }
+            mTrackers.put(user.getIdentifier(), new CalendarTracker(mContext, context));
+        }
+        evaluateSubscriptions();
+    }
+
     private void evaluateSubscriptions() {
-        if (DEBUG) Log.d(TAG, "evaluateSubscriptions");
+        if (!mWorker.hasCallbacks(mEvaluateSubscriptionsW)) {
+            mWorker.post(mEvaluateSubscriptionsW);
+        }
+    }
+
+    private void evaluateSubscriptionsW() {
+        if (DEBUG) Slog.d(TAG, "evaluateSubscriptions");
         if (!mBootComplete) {
-            if (DEBUG) Log.d(TAG, "Skipping evaluate before boot complete");
+            if (DEBUG) Slog.d(TAG, "Skipping evaluate before boot complete");
             return;
         }
         final long now = System.currentTimeMillis();
-        mTracker.setCallback(mSubscriptions.isEmpty() ? null : mTrackerCallback);
+        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) {
@@ -162,7 +210,30 @@
                 notifyCondition(conditionId, Condition.STATE_FALSE, "badConditionId");
                 continue;
             }
-            final CheckEventResult result = mTracker.checkEvent(event, now);
+            CheckEventResult result = null;
+            if (event.calendar == EventInfo.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");
+                    continue;
+                }
+                result = tracker.checkEvent(event, now);
+            }
             if (result.recheckAt != 0 && (reevaluateAt == 0 || result.recheckAt < reevaluateAt)) {
                 reevaluateAt = result.recheckAt;
             }
@@ -172,11 +243,12 @@
             }
             notifyCondition(conditionId, Condition.STATE_TRUE, "inEventNow");
         }
-        updateAlarm(now, reevaluateAt);
-        if (DEBUG) Log.d(TAG, "evaluateSubscriptions took " + (System.currentTimeMillis() - now));
+        rescheduleAlarm(now, reevaluateAt);
+        if (DEBUG) Slog.d(TAG, "evaluateSubscriptions took " + (System.currentTimeMillis() - now));
     }
 
-    private void updateAlarm(long now, long time) {
+    private void rescheduleAlarm(long now, long time) {
+        mNextAlarmTime = time;
         final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
                 REQUEST_CODE_EVALUATE,
@@ -196,8 +268,8 @@
     }
 
     private void notifyCondition(Uri conditionId, int state, String reason) {
-        if (DEBUG) Slog.d(TAG, "notifyCondition " + Condition.stateToString(state)
-                + " reason=" + reason);
+        if (DEBUG) Slog.d(TAG, "notifyCondition " + conditionId + " "
+                + Condition.stateToString(state) + " reason=" + reason);
         notifyCondition(createCondition(conditionId, state));
     }
 
@@ -223,19 +295,35 @@
         }
     }
 
+    private static Context getContextForUser(Context context, UserHandle user) {
+        try {
+            return context.createPackageContextAsUser(context.getPackageName(), 0, user);
+        } catch (NameNotFoundException e) {
+            return null;
+        }
+    }
+
     private final CalendarTracker.Callback mTrackerCallback = new CalendarTracker.Callback() {
         @Override
         public void onChanged() {
-            if (DEBUG) Log.d(TAG, "mTrackerCallback.onChanged");
-            evaluateSubscriptions();
+            if (DEBUG) Slog.d(TAG, "mTrackerCallback.onChanged");
+            mWorker.removeCallbacks(mEvaluateSubscriptionsW);
+            mWorker.postDelayed(mEvaluateSubscriptionsW, CHANGE_DELAY);
         }
     };
 
-    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             if (DEBUG) Slog.d(TAG, "onReceive " + intent.getAction());
             evaluateSubscriptions();
         }
     };
+
+    private final Runnable mEvaluateSubscriptionsW = new Runnable() {
+        @Override
+        public void run() {
+            evaluateSubscriptionsW();
+        }
+    };
 }