Loading services/core/java/com/android/server/notification/ZenModeFiltering.java +101 −32 Original line number Diff line number Diff line Loading @@ -24,11 +24,14 @@ import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.media.AudioAttributes; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings.Global; import android.service.notification.ZenModeConfig; import android.telecom.TelecomManager; import android.telephony.PhoneNumberUtils; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.Slog; Loading @@ -36,6 +39,8 @@ import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.util.NotificationMessagingUtil; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.Date; public class ZenModeFiltering { Loading Loading @@ -64,13 +69,22 @@ public class ZenModeFiltering { pw.print(prefix); pw.print("RepeatCallers.mThresholdMinutes="); pw.println(REPEAT_CALLERS.mThresholdMinutes); synchronized (REPEAT_CALLERS) { if (!REPEAT_CALLERS.mCalls.isEmpty()) { pw.print(prefix); pw.println("RepeatCallers.mCalls="); for (int i = 0; i < REPEAT_CALLERS.mCalls.size(); i++) { if (!REPEAT_CALLERS.mTelCalls.isEmpty()) { pw.print(prefix); pw.println("RepeatCallers.mTelCalls="); for (int i = 0; i < REPEAT_CALLERS.mTelCalls.size(); i++) { pw.print(prefix); pw.print(" "); pw.print(REPEAT_CALLERS.mCalls.keyAt(i)); pw.print(REPEAT_CALLERS.mTelCalls.keyAt(i)); pw.print(" at "); pw.println(ts(REPEAT_CALLERS.mCalls.valueAt(i))); pw.println(ts(REPEAT_CALLERS.mTelCalls.valueAt(i))); } } if (!REPEAT_CALLERS.mOtherCalls.isEmpty()) { pw.print(prefix); pw.println("RepeatCallers.mOtherCalls="); for (int i = 0; i < REPEAT_CALLERS.mOtherCalls.size(); i++) { pw.print(prefix); pw.print(" "); pw.print(REPEAT_CALLERS.mOtherCalls.keyAt(i)); pw.print(" at "); pw.println(ts(REPEAT_CALLERS.mOtherCalls.valueAt(i))); } } } Loading Loading @@ -330,34 +344,39 @@ public class ZenModeFiltering { } private static class RepeatCallers { // Person : time private final ArrayMap<String, Long> mCalls = new ArrayMap<>(); // We keep a separate map per uri scheme to do more generous number-matching // handling on telephone numbers specifically. For other inputs, we // simply match directly on the string. private final ArrayMap<String, Long> mTelCalls = new ArrayMap<>(); private final ArrayMap<String, Long> mOtherCalls = 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 String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); if (extraPeople == null || extraPeople.length == 0) return; final long now = System.currentTimeMillis(); cleanUp(mCalls, now); mCalls.put(peopleString, now); cleanUp(mTelCalls, now); cleanUp(mOtherCalls, now); recordCallers(extraPeople, 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 String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); if (extraPeople == null || extraPeople.length == 0) return false; final long now = System.currentTimeMillis(); cleanUp(mCalls, now); return mCalls.containsKey(peopleString); cleanUp(mTelCalls, now); cleanUp(mOtherCalls, now); return checkCallers(context, extraPeople); } 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); final long time = calls.valueAt(i); if (time > now || (now - time) > mThresholdMinutes * 1000 * 60) { calls.removeAt(i); } Loading @@ -367,10 +386,16 @@ public class ZenModeFiltering { // Clean up all calls that occurred after the given time. // Used only for tests, to clean up after testing. private synchronized void cleanUpCallsAfter(long timeThreshold) { for (int i = mCalls.size() - 1; i >= 0; i--) { final long time = mCalls.valueAt(i); for (int i = mTelCalls.size() - 1; i >= 0; i--) { final long time = mTelCalls.valueAt(i); if (time > timeThreshold) { mCalls.removeAt(i); mTelCalls.removeAt(i); } } for (int j = mOtherCalls.size() - 1; j >= 0; j--) { final long time = mOtherCalls.valueAt(j); if (time > timeThreshold) { mOtherCalls.removeAt(j); } } } Loading @@ -382,21 +407,65 @@ public class ZenModeFiltering { } } private static String peopleString(Bundle extras) { final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); if (extraPeople == null || extraPeople.length == 0) return null; final StringBuilder sb = new StringBuilder(); for (int i = 0; i < extraPeople.length; i++) { String extraPerson = extraPeople[i]; if (extraPerson == null) continue; extraPerson = extraPerson.trim(); if (extraPerson.isEmpty()) continue; if (sb.length() > 0) { sb.append('|'); } sb.append(extraPerson); } return sb.length() == 0 ? null : sb.toString(); private synchronized void recordCallers(String[] people, long now) { for (int i = 0; i < people.length; i++) { String person = people[i]; if (person == null) continue; final Uri uri = Uri.parse(person); if ("tel".equals(uri.getScheme())) { String tel = uri.getSchemeSpecificPart(); // while ideally we should not need to do this, sometimes we have seen tel // numbers given in a url-encoded format try { tel = URLDecoder.decode(tel, "UTF-8"); } catch (UnsupportedEncodingException e) { // ignore, keep the original tel string Slog.w(TAG, "unsupported encoding in tel: uri input"); } mTelCalls.put(tel, now); } else { // for non-tel calls, store the entire string, uri-component and all mOtherCalls.put(person, now); } } } private synchronized boolean checkCallers(Context context, String[] people) { // get the default country code for checking telephone numbers final String defaultCountryCode = context.getSystemService(TelephonyManager.class).getNetworkCountryIso(); for (int i = 0; i < people.length; i++) { String person = people[i]; if (person == null) continue; final Uri uri = Uri.parse(person); if ("tel".equals(uri.getScheme())) { String number = uri.getSchemeSpecificPart(); if (mTelCalls.containsKey(number)) { // check directly via map first return true; } else { // see if a number that matches via areSameNumber exists String numberToCheck = number; try { numberToCheck = URLDecoder.decode(number, "UTF-8"); } catch (UnsupportedEncodingException e) { // ignore, continue to use the original string Slog.w(TAG, "unsupported encoding in tel: uri input"); } for (String prev : mTelCalls.keySet()) { if (PhoneNumberUtils.areSamePhoneNumber( numberToCheck, prev, defaultCountryCode)) { return true; } } } } else { if (mOtherCalls.containsKey(person)) { return true; } } } return false; } } Loading services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java +132 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.server.notification; import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.CATEGORY_MESSAGE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE; import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT; Loading @@ -25,6 +24,7 @@ import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; Loading @@ -43,8 +43,10 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager.Policy; import android.media.AudioAttributes; import android.os.Bundle; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; Loading @@ -68,10 +70,15 @@ public class ZenModeFilteringTest extends UiServiceTestCase { private NotificationMessagingUtil mMessagingUtil; private ZenModeFiltering mZenModeFiltering; @Mock private TelephonyManager mTelephonyManager; @Before public void setUp() { MockitoAnnotations.initMocks(this); mZenModeFiltering = new ZenModeFiltering(mContext, mMessagingUtil); // for repeat callers / matchesCallFilter mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager); } private NotificationRecord getNotificationRecord() { Loading @@ -95,6 +102,23 @@ public class ZenModeFilteringTest extends UiServiceTestCase { return r; } private Bundle makeExtrasBundleWithPeople(String[] people) { Bundle extras = new Bundle(); extras.putObject(Notification.EXTRA_PEOPLE_LIST, people); return extras; } private NotificationRecord getNotificationRecordWithPeople(String[] people) { // set up notification record NotificationRecord r = mock(NotificationRecord.class); StatusBarNotification sbn = mock(StatusBarNotification.class); Notification notification = mock(Notification.class); notification.extras = makeExtrasBundleWithPeople(people); when(sbn.getNotification()).thenReturn(notification); when(r.getSbn()).thenReturn(sbn); return r; } @Test public void testIsMessage() { NotificationRecord r = getNotificationRecord(); Loading Loading @@ -309,4 +333,111 @@ public class ZenModeFilteringTest extends UiServiceTestCase { assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r)); } @Test public void testMatchesCallFilter_repeatCallers_directMatch() { // after calls given an email with an exact string match, make sure that // matchesCallFilter returns the right thing String[] mailSource = new String[]{"mailto:hello.world"}; mZenModeFiltering.recordCall(getNotificationRecordWithPeople(mailSource)); // set up policy to only allow repeat callers Policy policy = new Policy( PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); // check whether matchesCallFilter returns the right thing Bundle inputMatches = makeExtrasBundleWithPeople(new String[]{"mailto:hello.world"}); Bundle inputWrong = makeExtrasBundleWithPeople(new String[]{"mailto:nope"}); assertTrue(ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, inputMatches, null, 0, 0)); assertFalse(ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, inputWrong, null, 0, 0)); } @Test public void testMatchesCallFilter_repeatCallers_telephoneVariants() { // set up telephony manager behavior when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); String[] telSource = new String[]{"tel:+1-617-555-1212"}; mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource)); // set up policy to only allow repeat callers Policy policy = new Policy( PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); // cases to test: // - identical number // - same number, different formatting // - different number // - garbage Bundle identical = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"}); Bundle same = makeExtrasBundleWithPeople(new String[]{"tel:16175551212"}); Bundle different = makeExtrasBundleWithPeople(new String[]{"tel:123-456-7890"}); Bundle garbage = makeExtrasBundleWithPeople(new String[]{"asdfghjkl;"}); assertTrue("identical numbers should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, identical, null, 0, 0)); assertTrue("equivalent but non-identical numbers should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same, null, 0, 0)); assertFalse("non-equivalent numbers should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, different, null, 0, 0)); assertFalse("non-tel strings should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, garbage, null, 0, 0)); } @Test public void testMatchesCallFilter_repeatCallers_urlEncodedTels() { // this is not intended to be a supported case but is one that we have seen // sometimes in the wild, so make sure we handle url-encoded telephone numbers correctly // when somebody provides one. // set up telephony manager behavior when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); String[] telSource = new String[]{"tel:%2B16175551212"}; mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource)); // set up policy to only allow repeat callers Policy policy = new Policy( PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); // test cases for various forms of the same phone number and different ones Bundle same1 = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"}); Bundle same2 = makeExtrasBundleWithPeople(new String[]{"tel:%2B1-617-555-1212"}); Bundle same3 = makeExtrasBundleWithPeople(new String[]{"tel:6175551212"}); Bundle different1 = makeExtrasBundleWithPeople(new String[]{"tel:%2B16175553434"}); Bundle different2 = makeExtrasBundleWithPeople(new String[]{"tel:+16175553434"}); assertTrue("same number should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same1, null, 0, 0)); assertTrue("same number should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same2, null, 0, 0)); assertTrue("same number should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same3, null, 0, 0)); assertFalse("different number should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, different1, null, 0, 0)); assertFalse("different number should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, different2, null, 0, 0)); } } Loading
services/core/java/com/android/server/notification/ZenModeFiltering.java +101 −32 Original line number Diff line number Diff line Loading @@ -24,11 +24,14 @@ import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.media.AudioAttributes; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings.Global; import android.service.notification.ZenModeConfig; import android.telecom.TelecomManager; import android.telephony.PhoneNumberUtils; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.Slog; Loading @@ -36,6 +39,8 @@ import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.util.NotificationMessagingUtil; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.Date; public class ZenModeFiltering { Loading Loading @@ -64,13 +69,22 @@ public class ZenModeFiltering { pw.print(prefix); pw.print("RepeatCallers.mThresholdMinutes="); pw.println(REPEAT_CALLERS.mThresholdMinutes); synchronized (REPEAT_CALLERS) { if (!REPEAT_CALLERS.mCalls.isEmpty()) { pw.print(prefix); pw.println("RepeatCallers.mCalls="); for (int i = 0; i < REPEAT_CALLERS.mCalls.size(); i++) { if (!REPEAT_CALLERS.mTelCalls.isEmpty()) { pw.print(prefix); pw.println("RepeatCallers.mTelCalls="); for (int i = 0; i < REPEAT_CALLERS.mTelCalls.size(); i++) { pw.print(prefix); pw.print(" "); pw.print(REPEAT_CALLERS.mCalls.keyAt(i)); pw.print(REPEAT_CALLERS.mTelCalls.keyAt(i)); pw.print(" at "); pw.println(ts(REPEAT_CALLERS.mCalls.valueAt(i))); pw.println(ts(REPEAT_CALLERS.mTelCalls.valueAt(i))); } } if (!REPEAT_CALLERS.mOtherCalls.isEmpty()) { pw.print(prefix); pw.println("RepeatCallers.mOtherCalls="); for (int i = 0; i < REPEAT_CALLERS.mOtherCalls.size(); i++) { pw.print(prefix); pw.print(" "); pw.print(REPEAT_CALLERS.mOtherCalls.keyAt(i)); pw.print(" at "); pw.println(ts(REPEAT_CALLERS.mOtherCalls.valueAt(i))); } } } Loading Loading @@ -330,34 +344,39 @@ public class ZenModeFiltering { } private static class RepeatCallers { // Person : time private final ArrayMap<String, Long> mCalls = new ArrayMap<>(); // We keep a separate map per uri scheme to do more generous number-matching // handling on telephone numbers specifically. For other inputs, we // simply match directly on the string. private final ArrayMap<String, Long> mTelCalls = new ArrayMap<>(); private final ArrayMap<String, Long> mOtherCalls = 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 String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); if (extraPeople == null || extraPeople.length == 0) return; final long now = System.currentTimeMillis(); cleanUp(mCalls, now); mCalls.put(peopleString, now); cleanUp(mTelCalls, now); cleanUp(mOtherCalls, now); recordCallers(extraPeople, 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 String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); if (extraPeople == null || extraPeople.length == 0) return false; final long now = System.currentTimeMillis(); cleanUp(mCalls, now); return mCalls.containsKey(peopleString); cleanUp(mTelCalls, now); cleanUp(mOtherCalls, now); return checkCallers(context, extraPeople); } 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); final long time = calls.valueAt(i); if (time > now || (now - time) > mThresholdMinutes * 1000 * 60) { calls.removeAt(i); } Loading @@ -367,10 +386,16 @@ public class ZenModeFiltering { // Clean up all calls that occurred after the given time. // Used only for tests, to clean up after testing. private synchronized void cleanUpCallsAfter(long timeThreshold) { for (int i = mCalls.size() - 1; i >= 0; i--) { final long time = mCalls.valueAt(i); for (int i = mTelCalls.size() - 1; i >= 0; i--) { final long time = mTelCalls.valueAt(i); if (time > timeThreshold) { mCalls.removeAt(i); mTelCalls.removeAt(i); } } for (int j = mOtherCalls.size() - 1; j >= 0; j--) { final long time = mOtherCalls.valueAt(j); if (time > timeThreshold) { mOtherCalls.removeAt(j); } } } Loading @@ -382,21 +407,65 @@ public class ZenModeFiltering { } } private static String peopleString(Bundle extras) { final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); if (extraPeople == null || extraPeople.length == 0) return null; final StringBuilder sb = new StringBuilder(); for (int i = 0; i < extraPeople.length; i++) { String extraPerson = extraPeople[i]; if (extraPerson == null) continue; extraPerson = extraPerson.trim(); if (extraPerson.isEmpty()) continue; if (sb.length() > 0) { sb.append('|'); } sb.append(extraPerson); } return sb.length() == 0 ? null : sb.toString(); private synchronized void recordCallers(String[] people, long now) { for (int i = 0; i < people.length; i++) { String person = people[i]; if (person == null) continue; final Uri uri = Uri.parse(person); if ("tel".equals(uri.getScheme())) { String tel = uri.getSchemeSpecificPart(); // while ideally we should not need to do this, sometimes we have seen tel // numbers given in a url-encoded format try { tel = URLDecoder.decode(tel, "UTF-8"); } catch (UnsupportedEncodingException e) { // ignore, keep the original tel string Slog.w(TAG, "unsupported encoding in tel: uri input"); } mTelCalls.put(tel, now); } else { // for non-tel calls, store the entire string, uri-component and all mOtherCalls.put(person, now); } } } private synchronized boolean checkCallers(Context context, String[] people) { // get the default country code for checking telephone numbers final String defaultCountryCode = context.getSystemService(TelephonyManager.class).getNetworkCountryIso(); for (int i = 0; i < people.length; i++) { String person = people[i]; if (person == null) continue; final Uri uri = Uri.parse(person); if ("tel".equals(uri.getScheme())) { String number = uri.getSchemeSpecificPart(); if (mTelCalls.containsKey(number)) { // check directly via map first return true; } else { // see if a number that matches via areSameNumber exists String numberToCheck = number; try { numberToCheck = URLDecoder.decode(number, "UTF-8"); } catch (UnsupportedEncodingException e) { // ignore, continue to use the original string Slog.w(TAG, "unsupported encoding in tel: uri input"); } for (String prev : mTelCalls.keySet()) { if (PhoneNumberUtils.areSamePhoneNumber( numberToCheck, prev, defaultCountryCode)) { return true; } } } } else { if (mOtherCalls.containsKey(person)) { return true; } } } return false; } } Loading
services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java +132 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.server.notification; import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.CATEGORY_MESSAGE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE; import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT; Loading @@ -25,6 +24,7 @@ import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; Loading @@ -43,8 +43,10 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager.Policy; import android.media.AudioAttributes; import android.os.Bundle; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; Loading @@ -68,10 +70,15 @@ public class ZenModeFilteringTest extends UiServiceTestCase { private NotificationMessagingUtil mMessagingUtil; private ZenModeFiltering mZenModeFiltering; @Mock private TelephonyManager mTelephonyManager; @Before public void setUp() { MockitoAnnotations.initMocks(this); mZenModeFiltering = new ZenModeFiltering(mContext, mMessagingUtil); // for repeat callers / matchesCallFilter mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager); } private NotificationRecord getNotificationRecord() { Loading @@ -95,6 +102,23 @@ public class ZenModeFilteringTest extends UiServiceTestCase { return r; } private Bundle makeExtrasBundleWithPeople(String[] people) { Bundle extras = new Bundle(); extras.putObject(Notification.EXTRA_PEOPLE_LIST, people); return extras; } private NotificationRecord getNotificationRecordWithPeople(String[] people) { // set up notification record NotificationRecord r = mock(NotificationRecord.class); StatusBarNotification sbn = mock(StatusBarNotification.class); Notification notification = mock(Notification.class); notification.extras = makeExtrasBundleWithPeople(people); when(sbn.getNotification()).thenReturn(notification); when(r.getSbn()).thenReturn(sbn); return r; } @Test public void testIsMessage() { NotificationRecord r = getNotificationRecord(); Loading Loading @@ -309,4 +333,111 @@ public class ZenModeFilteringTest extends UiServiceTestCase { assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r)); } @Test public void testMatchesCallFilter_repeatCallers_directMatch() { // after calls given an email with an exact string match, make sure that // matchesCallFilter returns the right thing String[] mailSource = new String[]{"mailto:hello.world"}; mZenModeFiltering.recordCall(getNotificationRecordWithPeople(mailSource)); // set up policy to only allow repeat callers Policy policy = new Policy( PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); // check whether matchesCallFilter returns the right thing Bundle inputMatches = makeExtrasBundleWithPeople(new String[]{"mailto:hello.world"}); Bundle inputWrong = makeExtrasBundleWithPeople(new String[]{"mailto:nope"}); assertTrue(ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, inputMatches, null, 0, 0)); assertFalse(ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, inputWrong, null, 0, 0)); } @Test public void testMatchesCallFilter_repeatCallers_telephoneVariants() { // set up telephony manager behavior when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); String[] telSource = new String[]{"tel:+1-617-555-1212"}; mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource)); // set up policy to only allow repeat callers Policy policy = new Policy( PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); // cases to test: // - identical number // - same number, different formatting // - different number // - garbage Bundle identical = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"}); Bundle same = makeExtrasBundleWithPeople(new String[]{"tel:16175551212"}); Bundle different = makeExtrasBundleWithPeople(new String[]{"tel:123-456-7890"}); Bundle garbage = makeExtrasBundleWithPeople(new String[]{"asdfghjkl;"}); assertTrue("identical numbers should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, identical, null, 0, 0)); assertTrue("equivalent but non-identical numbers should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same, null, 0, 0)); assertFalse("non-equivalent numbers should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, different, null, 0, 0)); assertFalse("non-tel strings should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, garbage, null, 0, 0)); } @Test public void testMatchesCallFilter_repeatCallers_urlEncodedTels() { // this is not intended to be a supported case but is one that we have seen // sometimes in the wild, so make sure we handle url-encoded telephone numbers correctly // when somebody provides one. // set up telephony manager behavior when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); String[] telSource = new String[]{"tel:%2B16175551212"}; mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource)); // set up policy to only allow repeat callers Policy policy = new Policy( PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); // test cases for various forms of the same phone number and different ones Bundle same1 = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"}); Bundle same2 = makeExtrasBundleWithPeople(new String[]{"tel:%2B1-617-555-1212"}); Bundle same3 = makeExtrasBundleWithPeople(new String[]{"tel:6175551212"}); Bundle different1 = makeExtrasBundleWithPeople(new String[]{"tel:%2B16175553434"}); Bundle different2 = makeExtrasBundleWithPeople(new String[]{"tel:+16175553434"}); assertTrue("same number should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same1, null, 0, 0)); assertTrue("same number should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same2, null, 0, 0)); assertTrue("same number should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same3, null, 0, 0)); assertFalse("different number should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, different1, null, 0, 0)); assertFalse("different number should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, different2, null, 0, 0)); } }