Prevent notifications from erroroneously bypassing 'repeat callers' filter

1. Dialer uses a hidden api to check 'is repeat caller' that has side
effects. Update to use a method without side effects
2. Delay the storage of person information until the notification is
canceled, so ranking updates and notification updates don't bypass the
filter. Note, this means that someone calling twice at overlapping
times (?!) wouldn't get through the filter.

Fixes: 29184211
Change-Id: I53ce402a0ed6f5df6f8265d571d401778dacebd3
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cbd0769..e368af2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3150,6 +3150,12 @@
         }
     }
 
+    private void recordCallerLocked(NotificationRecord record) {
+        if (mZenModeHelper.isCall(record)) {
+            mZenModeHelper.recordCaller(record);
+        }
+    }
+
     // let zen mode evaluate this record
     private void applyZenModeLocked(NotificationRecord record) {
         record.setIntercepted(mZenModeHelper.shouldIntercept(record));
@@ -3289,6 +3295,10 @@
     }
 
     private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
+
+        // Record caller.
+        recordCallerLocked(r);
+
         // tell the app
         if (sendDelete) {
             if (r.getNotification().deleteIntent != null) {
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index 80dc523..cbaad46 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -81,7 +81,9 @@
         if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
         if (zen == Global.ZEN_MODE_ALARMS) return false; // not an alarm
         if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
-            if (config.allowRepeatCallers && REPEAT_CALLERS.isRepeat(context, extras)) return true;
+            if (config.allowRepeatCallers && REPEAT_CALLERS.isRepeat(context, extras)) {
+                return true;
+            }
             if (!config.allowCalls) return false; // no other calls get through
             if (validator != null) {
                 final float contactAffinity = validator.getContactAffinity(userHandle, extras,
@@ -97,6 +99,10 @@
                 ? record.sbn.getNotification().extras : null;
     }
 
+    protected void recordCall(NotificationRecord record) {
+        REPEAT_CALLERS.recordCall(mContext, extras(record));
+    }
+
     public boolean shouldIntercept(int zen, ZenModeConfig config, NotificationRecord record) {
         if (isSystem(record)) {
             return false;
@@ -233,28 +239,45 @@
     }
 
     private static class RepeatCallers {
+        // Person : time
         private final ArrayMap<String, Long> mCalls = new ArrayMap<>();
         private int mThresholdMinutes;
 
+        private synchronized void recordCall(Context context, Bundle extras) {
+            setThresholdMinutes(context);
+            if (mThresholdMinutes <= 0 || extras == null) return;
+            final String peopleString = peopleString(extras);
+            if (peopleString == null) return;
+            final long now = System.currentTimeMillis();
+            cleanUp(mCalls, now);
+            mCalls.put(peopleString, now);
+        }
+
         private synchronized boolean isRepeat(Context context, Bundle extras) {
-            if (mThresholdMinutes <= 0) {
-                mThresholdMinutes = context.getResources().getInteger(com.android.internal.R.integer
-                        .config_zen_repeat_callers_threshold);
-            }
+            setThresholdMinutes(context);
             if (mThresholdMinutes <= 0 || extras == null) return false;
             final String peopleString = peopleString(extras);
             if (peopleString == null) return false;
             final long now = System.currentTimeMillis();
-            final int N = mCalls.size();
+            cleanUp(mCalls, now);
+            return mCalls.containsKey(peopleString);
+        }
+
+        private synchronized void cleanUp(ArrayMap<String, Long> calls, long now) {
+            final int N = calls.size();
             for (int i = N - 1; i >= 0; i--) {
                 final long time = mCalls.valueAt(i);
                 if (time > now || (now - time) > mThresholdMinutes * 1000 * 60) {
-                    mCalls.removeAt(i);
+                    calls.removeAt(i);
                 }
             }
-            final boolean isRepeat = mCalls.containsKey(peopleString);
-            mCalls.put(peopleString, now);
-            return isRepeat;
+        }
+
+        private void setThresholdMinutes(Context context) {
+            if (mThresholdMinutes <= 0) {
+                mThresholdMinutes = context.getResources().getInteger(com.android.internal.R.integer
+                        .config_zen_repeat_callers_threshold);
+            }
         }
 
         private static String peopleString(Bundle extras) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8c9dc3b..c22bfb3 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -139,8 +139,7 @@
             ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {
         synchronized (mConfig) {
             return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle,
-                    extras,
-                    validator, contactsTimeoutMs, timeoutAffinity);
+                    extras, validator, contactsTimeoutMs, timeoutAffinity);
         }
     }
 
@@ -148,6 +147,10 @@
         return mFiltering.isCall(record);
     }
 
+    public void recordCaller(NotificationRecord record) {
+        mFiltering.recordCall(record);
+    }
+
     public boolean shouldIntercept(NotificationRecord record) {
         synchronized (mConfig) {
             return mFiltering.shouldIntercept(mZenMode, mConfig, record);