Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6b371b6 authored by Julia Reynolds's avatar Julia Reynolds
Browse files

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
parent 862f109d
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -3150,6 +3150,12 @@ public class NotificationManagerService extends SystemService {
        }
    }

    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 @@ public class NotificationManagerService extends SystemService {
    }

    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {

        // Record caller.
        recordCallerLocked(r);

        // tell the app
        if (sendDelete) {
            if (r.getNotification().deleteIntent != null) {
+33 −10
Original line number Diff line number Diff line
@@ -81,7 +81,9 @@ public class ZenModeFiltering {
        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 @@ public class ZenModeFiltering {
                ? 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 @@ public class ZenModeFiltering {
    }

    private static class RepeatCallers {
        // Person : time
        private final ArrayMap<String, Long> mCalls = new ArrayMap<>();
        private int mThresholdMinutes;

        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);
        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) {
            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) {
+5 −2
Original line number Diff line number Diff line
@@ -139,8 +139,7 @@ public class ZenModeHelper {
            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 @@ public class ZenModeHelper {
        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);