1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16package com.android.providers.blockednumber;
17
18import static org.mockito.Matchers.anyInt;
19import static org.mockito.Matchers.anyString;
20import static org.mockito.Matchers.eq;
21import static org.mockito.Mockito.doReturn;
22import static org.mockito.Mockito.reset;
23import static org.mockito.Mockito.spy;
24import static org.mockito.Mockito.times;
25import static org.mockito.Mockito.verify;
26import static org.mockito.Mockito.when;
27
28import android.app.AppOpsManager;
29import android.content.ContentResolver;
30import android.content.ContentUris;
31import android.content.ContentValues;
32import android.content.pm.PackageManager;
33import android.database.ContentObserver;
34import android.database.Cursor;
35import android.database.sqlite.SQLiteException;
36import android.location.Country;
37import android.net.Uri;
38import android.os.PersistableBundle;
39import android.os.SystemProperties;
40import android.provider.BlockedNumberContract;
41import android.provider.BlockedNumberContract.BlockedNumbers;
42import android.provider.BlockedNumberContract.SystemContract;
43import android.telephony.CarrierConfigManager;
44import android.telephony.TelephonyManager;
45import android.test.AndroidTestCase;
46import android.test.MoreAsserts;
47import android.test.suitebuilder.annotation.MediumTest;
48
49import junit.framework.Assert;
50
51import java.util.concurrent.CountDownLatch;
52import java.util.concurrent.TimeUnit;
53
54/**
55 * runtest --path packages/providers/BlockedNumberProvider/tests
56 */
57@MediumTest
58public class BlockedNumberProviderTest extends AndroidTestCase {
59    private MyMockContext mMockContext;
60    private ContentResolver mResolver;
61
62    @Override
63    protected void setUp() throws Exception {
64        super.setUp();
65        BlockedNumberProvider.ALLOW_SELF_CALL = false;
66
67        mMockContext = spy(new MyMockContext(getContext()));
68        mMockContext.initializeContext();
69        mResolver = mMockContext.getContentResolver();
70
71        when(mMockContext.mUserManager.isPrimaryUser()).thenReturn(true);
72        when(mMockContext.mCountryDetector.detectCountry())
73                .thenReturn(new Country("US", Country.COUNTRY_SOURCE_LOCATION));
74        when(mMockContext.mAppOpsManager.noteOp(
75                eq(AppOpsManager.OP_WRITE_SMS), anyInt(), anyString()))
76                .thenReturn(AppOpsManager.MODE_ERRORED);
77    }
78
79    @Override
80    protected void tearDown() throws Exception {
81        mMockContext.shutdown();
82
83        super.tearDown();
84    }
85
86    private static ContentValues cv(Object... namesAndValues) {
87        Assert.assertTrue((namesAndValues.length % 2) == 0);
88
89        final ContentValues ret = new ContentValues();
90        for (int i = 1; i < namesAndValues.length; i += 2) {
91            final String name = namesAndValues[i - 1].toString();
92            final Object value = namesAndValues[i];
93            if (value == null) {
94                ret.putNull(name);
95            } else if (value instanceof String) {
96                ret.put(name, (String) value);
97            } else if (value instanceof Integer) {
98                ret.put(name, (Integer) value);
99            } else if (value instanceof Long) {
100                ret.put(name, (Long) value);
101            } else {
102                Assert.fail("Unsupported type: " + value.getClass().getSimpleName());
103            }
104        }
105        return ret;
106    }
107
108    private void assertRowCount(int count, Uri uri) {
109        try (Cursor c = mResolver.query(uri, null, null, null, null)) {
110            assertEquals(count, c.getCount());
111        }
112    }
113
114    public void testGetType() {
115        assertEquals(BlockedNumbers.CONTENT_TYPE, mResolver.getType(
116                BlockedNumbers.CONTENT_URI));
117
118        assertEquals(BlockedNumbers.CONTENT_ITEM_TYPE, mResolver.getType(
119                ContentUris.withAppendedId(BlockedNumbers.CONTENT_URI, 1)));
120
121        assertNull(mResolver.getType(
122                Uri.withAppendedPath(BlockedNumberContract.AUTHORITY_URI, "invalid")));
123    }
124
125    public void testInsert() {
126        insertExpectingFailure(cv());
127        insertExpectingFailure(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, null));
128        insertExpectingFailure(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, ""));
129        insertExpectingFailure(cv(BlockedNumbers.COLUMN_ID, 1));
130        insertExpectingFailure(cv(BlockedNumbers.COLUMN_E164_NUMBER, "1"));
131
132        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
133        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "+1-2-3"));
134        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "+1-408-454-1111"));
135        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1-408-454-2222"));
136        // Re-inserting the same number should be ok, but the E164 number is replaced.
137        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1-408-454-2222",
138                BlockedNumbers.COLUMN_E164_NUMBER, "+814084542222"));
139
140        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1-408-4542222"));
141
142        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "045-381-1111",
143                BlockedNumbers.COLUMN_E164_NUMBER, "+81453811111"));
144
145        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "12345"));
146
147
148
149        assertRowCount(7, BlockedNumbers.CONTENT_URI);
150
151        assertContents(1, "123", "");
152        assertContents(2, "+1-2-3", "");
153        assertContents(3, "+1-408-454-1111", "+14084541111");
154        // Missing 4 due to re-insertion of the same number.
155        assertContents(5, "1-408-454-2222", "+814084542222");
156        assertContents(6, "1-408-4542222", "+14084542222");
157        assertContents(7, "045-381-1111", "+81453811111");
158        assertContents(8, "12345", "");
159    }
160
161    public void testChangesNotified() throws Exception {
162        Cursor c = mResolver.query(BlockedNumbers.CONTENT_URI, null, null, null, null);
163
164        final CountDownLatch latch = new CountDownLatch(2);
165        ContentObserver contentObserver = new ContentObserver(null) {
166            @Override
167            public void onChange(boolean selfChange) {
168                Assert.assertFalse(selfChange);
169                latch.notify();
170            }
171        };
172        c.registerContentObserver(contentObserver);
173
174        try {
175            Uri uri = insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "14506507000"));
176            mResolver.delete(uri, null, null);
177            latch.await(10, TimeUnit.SECONDS);
178            verify(mMockContext.mBackupManager, times(2)).dataChanged();
179        } catch (Exception e) {
180            fail(e.toString());
181        } finally {
182            c.unregisterContentObserver(contentObserver);
183        }
184    }
185
186    private Uri insert(ContentValues cv) {
187        final Uri uri = mResolver.insert(BlockedNumbers.CONTENT_URI, cv);
188        assertNotNull(uri);
189
190        // Make sure the URI exists.
191        try (Cursor c = mResolver.query(uri, null, null, null, null)) {
192            assertEquals(1, c.getCount());
193        }
194        return uri;
195    }
196
197    private void insertExpectingFailure(ContentValues cv) {
198        try {
199            mResolver.insert(
200                    BlockedNumbers.CONTENT_URI, cv);
201            fail();
202        } catch (IllegalArgumentException expected) {
203        }
204    }
205
206    public void testDelete() {
207        // Prepare test data
208        Uri u1 = insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
209        Uri u2 = insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "+1-2-3"));
210        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "+1-408-454-1111"));
211        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1-408-454-2222"));
212
213        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "045-381-1111",
214                BlockedNumbers.COLUMN_E164_NUMBER, "12345"));
215
216        assertRowCount(5, BlockedNumbers.CONTENT_URI);
217
218        // Delete and check the # of remaining rows.
219
220        mResolver.delete(u1, null, null);
221        assertRowCount(4, BlockedNumbers.CONTENT_URI);
222
223        try {
224            mResolver.delete(u2, "1=1", null);
225            fail();
226        } catch (IllegalArgumentException expected) {
227            MoreAsserts.assertContainsRegex("selection must be null", expected.getMessage());
228        }
229
230        mResolver.delete(u2, null, null);
231        assertRowCount(3, BlockedNumbers.CONTENT_URI);
232
233        mResolver.delete(BlockedNumbers.CONTENT_URI,
234                BlockedNumbers.COLUMN_E164_NUMBER + "=?",
235                new String[]{"12345"});
236        assertRowCount(2, BlockedNumbers.CONTENT_URI);
237
238        // SQL injection should be detected.
239        try {
240            mResolver.delete(BlockedNumbers.CONTENT_URI, "; DROP TABLE blocked; ", null);
241            fail();
242        } catch (SQLiteException expected) {
243        }
244        assertRowCount(2, BlockedNumbers.CONTENT_URI);
245
246        mResolver.delete(BlockedNumbers.CONTENT_URI, null, null);
247        assertRowCount(0, BlockedNumbers.CONTENT_URI);
248    }
249
250    public void testUpdate() {
251        try {
252            mResolver.update(BlockedNumbers.CONTENT_URI, cv(),
253                    /* selection =*/ null, /* args =*/ null);
254            fail();
255        } catch (UnsupportedOperationException expected) {
256            MoreAsserts.assertContainsRegex("Update is not supported", expected.getMessage());
257        }
258    }
259
260    public void testBlockSuppressionAfterEmergencyContact() {
261        int blockSuppressionSeconds = 1000;
262        when(mMockContext.mCarrierConfigManager.getConfig())
263                .thenReturn(getBundleWithInt(blockSuppressionSeconds));
264
265        String phoneNumber = "5004541111";
266        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, phoneNumber));
267
268        // No emergency contact: Blocks should not be suppressed.
269        assertIsBlocked(true, phoneNumber);
270        assertShouldSystemBlock(true, phoneNumber);
271        verifyBlocksNotSuppressed();
272        assertTrue(mMockContext.mIntentsBroadcasted.isEmpty());
273
274        // No emergency contact yet: Ending block suppression should be a no-op.
275        SystemContract.endBlockSuppression(mMockContext);
276        assertIsBlocked(true, phoneNumber);
277        assertShouldSystemBlock(true, phoneNumber);
278        verifyBlocksNotSuppressed();
279        assertTrue(mMockContext.mIntentsBroadcasted.isEmpty());
280
281        // After emergency contact blocks should be suppressed.
282        long timestampMillisBeforeEmergencyContact = System.currentTimeMillis();
283        SystemContract.notifyEmergencyContact(mMockContext);
284        assertIsBlocked(true, phoneNumber);
285        assertShouldSystemBlock(false, phoneNumber);
286        SystemContract.BlockSuppressionStatus status =
287                SystemContract.getBlockSuppressionStatus(mMockContext);
288        assertTrue(status.isSuppressed);
289        assertValidBlockSuppressionExpiration(timestampMillisBeforeEmergencyContact,
290                blockSuppressionSeconds, status.untilTimestampMillis);
291        assertEquals(1, mMockContext.mIntentsBroadcasted.size());
292        assertEquals(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED,
293                mMockContext.mIntentsBroadcasted.get(0));
294        mMockContext.mIntentsBroadcasted.clear();
295
296        // Ending block suppression should work.
297        SystemContract.endBlockSuppression(mMockContext);
298        assertIsBlocked(true, phoneNumber);
299        assertShouldSystemBlock(true, phoneNumber);
300        verifyBlocksNotSuppressed();
301        assertEquals(1, mMockContext.mIntentsBroadcasted.size());
302        assertEquals(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED,
303                mMockContext.mIntentsBroadcasted.get(0));
304    }
305
306    public void testBlockSuppressionAfterEmergencyContact_invalidCarrierConfigDefaultValueUsed() {
307        int invalidBlockSuppressionSeconds = 700000; // > 1 week
308        when(mMockContext.mCarrierConfigManager.getConfig())
309                .thenReturn(getBundleWithInt(invalidBlockSuppressionSeconds));
310
311        String phoneNumber = "5004541111";
312        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, phoneNumber));
313
314        long timestampMillisBeforeEmergencyContact = System.currentTimeMillis();
315        SystemContract.notifyEmergencyContact(mMockContext);
316        assertIsBlocked(true, phoneNumber);
317        assertShouldSystemBlock(false, phoneNumber);
318        SystemContract.BlockSuppressionStatus status =
319                SystemContract.getBlockSuppressionStatus(mMockContext);
320        assertTrue(status.isSuppressed);
321        assertValidBlockSuppressionExpiration(timestampMillisBeforeEmergencyContact,
322                7200 /* Default value of 2 hours */, status.untilTimestampMillis);
323        assertEquals(1, mMockContext.mIntentsBroadcasted.size());
324        assertEquals(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED,
325                mMockContext.mIntentsBroadcasted.get(0));
326    }
327
328    public void testRegularAppCannotAccessApis() {
329        doReturn(PackageManager.PERMISSION_DENIED)
330                .when(mMockContext).checkCallingPermission(anyString());
331
332        try {
333            insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
334            fail("SecurityException expected");
335        } catch (SecurityException expected) {
336        }
337
338        try {
339            mResolver.query(BlockedNumbers.CONTENT_URI, null, null, null, null);
340            fail("SecurityException expected");
341        } catch (SecurityException expected) {
342        }
343
344        try {
345            mResolver.delete(BlockedNumbers.CONTENT_URI, null, null);
346            fail("SecurityException expected");
347        } catch (SecurityException expected) {
348        }
349
350        try {
351            BlockedNumberContract.isBlocked(mMockContext, "123");
352            fail("SecurityException expected");
353        } catch (SecurityException expected) {
354        }
355
356        try {
357            BlockedNumberContract.unblock(mMockContext, "123");
358            fail("SecurityException expected");
359        } catch (SecurityException expected) {
360        }
361
362        try {
363            SystemContract.notifyEmergencyContact(mMockContext);
364            fail("SecurityException expected");
365        } catch (SecurityException expected) {
366        }
367
368        try {
369            SystemContract.endBlockSuppression(mMockContext);
370            fail("SecurityException expected");
371        } catch (SecurityException expected) {
372        }
373
374        try {
375            SystemContract.shouldSystemBlockNumber(mMockContext, "123");
376            fail("SecurityException expected");
377        } catch (SecurityException expected) {
378        }
379
380        try {
381            SystemContract.getBlockSuppressionStatus(mMockContext);
382            fail("SecurityException expected");
383        } catch (SecurityException expected) {
384        }
385    }
386
387    public void testCarrierAppCanAccessApis() {
388        doReturn(PackageManager.PERMISSION_DENIED)
389                .when(mMockContext).checkCallingPermission(anyString());
390        when(mMockContext.mTelephonyManager.checkCarrierPrivilegesForPackage(anyString()))
391                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
392
393        mResolver.insert(
394                BlockedNumbers.CONTENT_URI, cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
395        assertIsBlocked(true, "123");
396
397
398        // Dialer check is executed twice: once for insert, and once for isBlocked.
399        verify(mMockContext.mTelephonyManager, times(2))
400                .checkCarrierPrivilegesForPackage(anyString());
401    }
402
403    public void testSelfCanAccessApis() {
404        BlockedNumberProvider.ALLOW_SELF_CALL = true;
405        doReturn(PackageManager.PERMISSION_DENIED)
406                .when(mMockContext).checkCallingPermission(anyString());
407
408        mResolver.insert(
409                BlockedNumbers.CONTENT_URI, cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
410        assertIsBlocked(true, "123");
411    }
412
413    public void testDefaultDialerCanAccessApis() {
414        doReturn(PackageManager.PERMISSION_DENIED)
415                .when(mMockContext).checkCallingPermission(anyString());
416        when(mMockContext.mTelecomManager.getDefaultDialerPackage())
417                .thenReturn(getContext().getPackageName());
418
419        mResolver.insert(
420                BlockedNumbers.CONTENT_URI, cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
421        assertIsBlocked(true, "123");
422
423        // Dialer check is executed twice: once for insert, and once for isBlocked.
424        verify(mMockContext.mTelecomManager, times(2)).getDefaultDialerPackage();
425    }
426
427    public void testPrivilegedAppCannotUseSystemApis() {
428        reset(mMockContext.mAppOpsManager);
429        doReturn(PackageManager.PERMISSION_DENIED)
430                .when(mMockContext).checkCallingPermission(anyString());
431
432        // Pretend to be the Default SMS app.
433        when(mMockContext.mAppOpsManager.noteOp(
434                eq(AppOpsManager.OP_WRITE_SMS), anyInt(), anyString()))
435                .thenReturn(AppOpsManager.MODE_ALLOWED);
436
437        // Public APIs should work.
438        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
439        assertIsBlocked(true, "123");
440
441        try {
442            SystemContract.notifyEmergencyContact(mMockContext);
443            fail("SecurityException expected");
444        } catch (SecurityException expected) {
445        }
446
447        try {
448            SystemContract.endBlockSuppression(mMockContext);
449            fail("SecurityException expected");
450        } catch (SecurityException expected) {
451        }
452
453        try {
454            SystemContract.shouldSystemBlockNumber(mMockContext, "123");
455            fail("SecurityException expected");
456        } catch (SecurityException expected) {
457        }
458
459        try {
460            SystemContract.getBlockSuppressionStatus(mMockContext);
461            fail("SecurityException expected");
462        } catch (SecurityException expected) {
463        }
464    }
465
466    public void testIsBlocked() {
467        assertTrue(BlockedNumberContract.canCurrentUserBlockNumbers(mMockContext));
468
469        // Prepare test data
470        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
471        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "+1.2-3"));
472        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "+1-500-454-1111"));
473        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1-500-454-2222"));
474
475        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "045-111-2222",
476                BlockedNumbers.COLUMN_E164_NUMBER, "+81451112222"));
477
478        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "abc.def@gmail.com"));
479
480        // Check
481        assertIsBlocked(false, "");
482        assertIsBlocked(false, null);
483        assertIsBlocked(true, "123");
484        assertIsBlocked(false, "1234");
485        assertIsBlocked(true, "+81451112222");
486        assertIsBlocked(true, "+81 45 111 2222");
487        assertIsBlocked(true, "045-111-2222");
488        assertIsBlocked(false, "045 111 2222");
489
490        assertIsBlocked(true, "500-454 1111");
491        assertIsBlocked(true, "500-454 2222");
492        assertIsBlocked(true, "+1 500-454 1111");
493        assertIsBlocked(true, "1 500-454 1111");
494
495        assertIsBlocked(true, "abc.def@gmail.com");
496        assertIsBlocked(false, "abc.def@gmail.co");
497        assertIsBlocked(false, "bc.def@gmail.com");
498        assertIsBlocked(false, "abcdef@gmail.com");
499    }
500
501    public void testUnblock() {
502        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "+1-500-454-1111"));
503        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1500-454-1111"));
504        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "abc.def@gmail.com"));
505
506        // Unblocking non-existent number is a no-op.
507        assertEquals(0, BlockedNumberContract.unblock(mMockContext, "12345"));
508
509        // Both rows which map to the same E164 number are deleted.
510        assertEquals(2, BlockedNumberContract.unblock(mMockContext, "5004541111"));
511        assertIsBlocked(false, "1-500-454-1111");
512
513        assertEquals(1, BlockedNumberContract.unblock(mMockContext, "abc.def@gmail.com"));
514        assertIsBlocked(false, "abc.def@gmail.com");
515    }
516
517    public void testEmergencyNumbersAreNotBlockedBySystem() {
518        String emergencyNumber = getEmergencyNumberFromSystemPropertiesOrDefault();
519        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, emergencyNumber));
520
521        assertIsBlocked(true, emergencyNumber);
522        assertFalse(SystemContract.shouldSystemBlockNumber(mMockContext, emergencyNumber));
523    }
524
525    public void testPrivilegedAppAccessingApisAsSecondaryUser() {
526        when(mMockContext.mUserManager.isPrimaryUser()).thenReturn(false);
527
528        assertFalse(BlockedNumberContract.canCurrentUserBlockNumbers(mMockContext));
529
530        try {
531            insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
532            fail("SecurityException expected");
533        } catch (SecurityException expected) {
534            assertTrue(expected.getMessage().contains("current user"));
535        }
536
537        try {
538            mResolver.query(BlockedNumbers.CONTENT_URI, null, null, null, null);
539            fail("SecurityException expected");
540        } catch (SecurityException expected) {
541        }
542
543        try {
544            mResolver.delete(BlockedNumbers.CONTENT_URI, null, null);
545            fail("SecurityException expected");
546        } catch (SecurityException expected) {
547        }
548
549        try {
550            BlockedNumberContract.isBlocked(mMockContext, "123");
551            fail("SecurityException expected");
552        } catch (SecurityException expected) {
553        }
554
555        try {
556            BlockedNumberContract.unblock(mMockContext, "123");
557            fail("SecurityException expected");
558        } catch (SecurityException expected) {
559        }
560    }
561
562    public void testRegularAppAccessingApisAsSecondaryUser() {
563        when(mMockContext.mUserManager.isPrimaryUser()).thenReturn(false);
564        doReturn(PackageManager.PERMISSION_DENIED)
565                .when(mMockContext).checkCallingPermission(anyString());
566
567        assertFalse(BlockedNumberContract.canCurrentUserBlockNumbers(mMockContext));
568
569        try {
570            insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
571            fail("SecurityException expected");
572        } catch (SecurityException expected) {
573        }
574
575        try {
576            mResolver.query(BlockedNumbers.CONTENT_URI, null, null, null, null);
577            fail("SecurityException expected");
578        } catch (SecurityException expected) {
579        }
580
581        try {
582            mResolver.delete(BlockedNumbers.CONTENT_URI, null, null);
583            fail("SecurityException expected");
584        } catch (SecurityException expected) {
585        }
586
587        try {
588            BlockedNumberContract.isBlocked(mMockContext, "123");
589            fail("SecurityException expected");
590        } catch (SecurityException expected) {
591        }
592
593        try {
594            BlockedNumberContract.unblock(mMockContext, "123");
595            fail("SecurityException expected");
596        } catch (SecurityException expected) {
597        }
598    }
599
600    private void assertIsBlocked(boolean expected, String phoneNumber) {
601        assertEquals(expected, BlockedNumberContract.isBlocked(mMockContext, phoneNumber));
602    }
603
604    private void assertShouldSystemBlock(boolean expected, String phoneNumber) {
605        assertEquals(expected, SystemContract.shouldSystemBlockNumber(mMockContext, phoneNumber));
606    }
607
608    private PersistableBundle getBundleWithInt(int value) {
609        PersistableBundle bundle = new PersistableBundle();
610        bundle.putInt(
611                CarrierConfigManager.KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, value);
612        return bundle;
613    }
614
615    private void verifyBlocksNotSuppressed() {
616        SystemContract.BlockSuppressionStatus status =
617                SystemContract.getBlockSuppressionStatus(mMockContext);
618        assertFalse(status.isSuppressed);
619        assertEquals(0, status.untilTimestampMillis);
620    }
621
622    private void assertValidBlockSuppressionExpiration(long timestampMillisBeforeEmergencyContact,
623                                                       int blockSuppressionSeconds,
624                                                       long actualExpirationMillis) {
625        assertTrue(actualExpirationMillis
626                >= timestampMillisBeforeEmergencyContact + blockSuppressionSeconds * 1000);
627        assertTrue(actualExpirationMillis < timestampMillisBeforeEmergencyContact +
628                2 * blockSuppressionSeconds * 1000);
629    }
630
631    private void assertContents(int rowId, String originalNumber, String e164Number) {
632        Uri uri = ContentUris.withAppendedId(BlockedNumbers.CONTENT_URI, rowId);
633        try (Cursor c = mResolver.query(uri, null, null, null, null)) {
634            assertEquals(1, c.getCount());
635            c.moveToNext();
636            assertEquals(3, c.getColumnCount());
637            assertEquals(rowId, c.getInt(c.getColumnIndex(BlockedNumbers.COLUMN_ID)));
638            assertEquals(originalNumber,
639                    c.getString(c.getColumnIndex(BlockedNumbers.COLUMN_ORIGINAL_NUMBER)));
640            assertEquals(e164Number,
641                    c.getString(c.getColumnIndex(BlockedNumbers.COLUMN_E164_NUMBER)));
642        }
643    }
644
645    private String getEmergencyNumberFromSystemPropertiesOrDefault() {
646        String systemEmergencyNumbers = SystemProperties.get("ril.ecclist");
647        if (systemEmergencyNumbers == null) {
648            return "911";
649        } else {
650            return systemEmergencyNumbers.split(",")[0];
651        }
652    }
653}
654