Home | History | Annotate | Download | only in contacts
      1 /*
      2  * Copyright (C) 2011 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  */
     16 
     17 package com.android.providers.contacts;
     18 
     19 import static org.mockito.Mockito.mock;
     20 import static org.mockito.Mockito.verify;
     21 
     22 import android.content.ContentUris;
     23 import android.content.ContentValues;
     24 import android.database.ContentObserver;
     25 import android.database.Cursor;
     26 import android.net.Uri;
     27 import android.os.Handler;
     28 import android.os.ParcelFileDescriptor;
     29 import android.os.Process;
     30 import android.provider.CallLog;
     31 import android.provider.CallLog.Calls;
     32 import android.provider.VoicemailContract;
     33 import android.provider.VoicemailContract.Status;
     34 import android.provider.VoicemailContract.Voicemails;
     35 import android.test.MoreAsserts;
     36 import android.test.suitebuilder.annotation.SmallTest;
     37 
     38 import com.android.common.io.MoreCloseables;
     39 
     40 import org.mockito.Mockito;
     41 
     42 import java.io.FileNotFoundException;
     43 import java.io.IOException;
     44 import java.io.InputStream;
     45 import java.io.OutputStream;
     46 import java.util.Arrays;
     47 import java.util.List;
     48 
     49 /**
     50  * Unit tests for {@link VoicemailContentProvider}.
     51  *
     52  * Run the test like this:
     53  * <code>
     54  * runtest -c com.android.providers.contacts.VoicemailProviderTest contactsprov
     55  * </code>
     56  */
     57 // TODO: Test that calltype and voicemail_uri are auto populated by the provider.
     58 @SmallTest
     59 public class VoicemailProviderTest extends BaseVoicemailProviderTest {
     60 
     61     private static final String SYSTEM_PROPERTY_DEXMAKER_DEXCACHE = "dexmaker.dexcache";
     62 
     63     /**
     64      * Fields specific to call_log provider that should not be exposed by voicemail provider.
     65      */
     66     private static final String[] CALLLOG_PROVIDER_SPECIFIC_COLUMNS = {
     67             Calls.CACHED_NAME,
     68             Calls.CACHED_NUMBER_LABEL,
     69             Calls.CACHED_NUMBER_TYPE,
     70             Calls.TYPE,
     71             Calls.VOICEMAIL_URI,
     72             Calls.COUNTRY_ISO
     73     };
     74     /**
     75      * Total number of columns exposed by voicemail provider.
     76      */
     77     private static final int NUM_VOICEMAIL_FIELDS = 25;
     78 
     79     @Override
     80     protected void setUp() throws Exception {
     81         super.setUp();
     82         setUpForOwnPermission();
     83         System.setProperty(SYSTEM_PROPERTY_DEXMAKER_DEXCACHE, getContext().getCacheDir().getPath());
     84         Thread.currentThread()
     85                 .setContextClassLoader(VoicemailContentProvider.class.getClassLoader());
     86         addProvider(CallLogProviderTestable.class, CallLog.AUTHORITY);
     87     }
     88 
     89     @Override
     90     protected void tearDown() throws Exception {
     91         System.clearProperty(SYSTEM_PROPERTY_DEXMAKER_DEXCACHE);
     92         DbModifierWithNotification.setVoicemailNotifierForTest(null);
     93     }
     94 
     95     /**
     96      * Returns the appropriate /voicemail URI.
     97      */
     98     private Uri voicemailUri() {
     99         return mUseSourceUri ?
    100                 Voicemails.buildSourceUri(mActor.packageName) : Voicemails.CONTENT_URI;
    101     }
    102 
    103     /**
    104      * Returns the appropriate /status URI.
    105      */
    106     private Uri statusUri() {
    107         return mUseSourceUri ?
    108                 Status.buildSourceUri(mActor.packageName) : Status.CONTENT_URI;
    109     }
    110 
    111     public void testInsert() throws Exception {
    112         setTimeForTest(1000L);
    113         Uri uri = mResolver.insert(voicemailUri(), getTestVoicemailValues());
    114         // We create on purpose a new set of ContentValues here, because the code above modifies
    115         // the copy it gets.
    116         assertStoredValues(uri, getTestVoicemailValues());
    117         assertSelection(uri, getTestVoicemailValues(), Voicemails._ID, ContentUris.parseId(uri));
    118         assertEquals(1, countFilesInTestDirectory());
    119 
    120         assertLastModified(uri, 1000);
    121     }
    122 
    123     public void testInsertReadMessageIsNotNew() throws Exception {
    124         ContentValues values = getTestReadVoicemailValues();
    125         values.remove(Voicemails.NEW);
    126         Uri uri = mResolver.insert(voicemailUri(), values);
    127         String[] projection = {Voicemails.NUMBER, Voicemails.DATE, Voicemails.DURATION,
    128                 Voicemails.TRANSCRIPTION, Voicemails.NEW, Voicemails.IS_READ,
    129                 Voicemails.HAS_CONTENT,
    130                 Voicemails.SOURCE_DATA, Voicemails.STATE,
    131                 Voicemails.BACKED_UP, Voicemails.RESTORED, Voicemails.ARCHIVED,
    132                 Voicemails.IS_OMTP_VOICEMAIL
    133         };
    134         Cursor c = mResolver.query(uri, projection, Voicemails.NEW + "=0", null,
    135                 null);
    136         try {
    137             assertEquals("Record count", 1, c.getCount());
    138             c.moveToFirst();
    139             assertEquals(1, countFilesInTestDirectory());
    140             assertCursorValues(c, values);
    141         } catch (Error e) {
    142             TestUtils.dumpCursor(c);
    143             throw e;
    144         } finally {
    145             c.close();
    146         }
    147     }
    148 
    149     public void testBulkInsert() {
    150         VoicemailNotifier notifier = mock(VoicemailNotifier.class);
    151         DbModifierWithNotification.setVoicemailNotifierForTest(notifier);
    152         mResolver.bulkInsert(voicemailUri(),
    153                 new ContentValues[] {getTestVoicemailValues(), getTestVoicemailValues()});
    154         verify(notifier, Mockito.times(1)).sendNotification();
    155     }
    156 
    157     // Test to ensure that media content can be written and read back.
    158     public void testFileContent() throws Exception {
    159         Uri uri = insertVoicemail();
    160         OutputStream out = mResolver.openOutputStream(uri);
    161         byte[] outBuffer = {0x1, 0x2, 0x3, 0x4};
    162         out.write(outBuffer);
    163         out.flush();
    164         out.close();
    165         InputStream in = mResolver.openInputStream(uri);
    166         byte[] inBuffer = new byte[4];
    167         int numBytesRead = in.read(inBuffer);
    168         assertEquals(numBytesRead, outBuffer.length);
    169         MoreAsserts.assertEquals(outBuffer, inBuffer);
    170         // No more data should be left.
    171         assertEquals(-1, in.read(inBuffer));
    172         in.close();
    173     }
    174 
    175     public void testUpdate() {
    176         setTimeForTest(1000L);
    177         Uri uri = insertVoicemail();
    178         ContentValues values = new ContentValues();
    179         values.put(Voicemails.NUMBER, "1-800-263-7643");
    180         values.put(Voicemails.DATE, 2000);
    181         values.put(Voicemails.DURATION, 40);
    182         values.put(Voicemails.TRANSCRIPTION, "Testing 123");
    183         values.put(Voicemails.STATE, 2);
    184         values.put(Voicemails.HAS_CONTENT, 1);
    185         values.put(Voicemails.SOURCE_DATA, "foo");
    186         values.put(Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, "dummy_name");
    187         values.put(Voicemails.PHONE_ACCOUNT_ID, "dummy_account");
    188         values.put(Voicemails.BACKED_UP, 1);
    189         values.put(Voicemails.RESTORED, 1);
    190         values.put(Voicemails.ARCHIVED, 1);
    191         values.put(Voicemails.IS_OMTP_VOICEMAIL, 1);
    192         int count = mResolver.update(uri, values, null, null);
    193         assertEquals(1, count);
    194         assertStoredValues(uri, values);
    195         assertLastModified(uri, 1000);
    196     }
    197 
    198     public void testUpdateOwnPackageVoicemail_NotDirty() {
    199         final Uri uri = mResolver.insert(voicemailUri(), getTestVoicemailValues());
    200         ContentValues updateValues = new ContentValues();
    201         updateValues.put(Voicemails.TRANSCRIPTION, "foo");
    202         mResolver.update(uri, updateValues, null, null);
    203 
    204         // Updating a package's own voicemail should not make the voicemail dirty.
    205         try (Cursor cursor = mResolver
    206                 .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
    207             cursor.moveToFirst();
    208             assertEquals(cursor.getInt(0), 0);
    209         }
    210     }
    211 
    212     public void testUpdateOtherPackageCallLog_NotDirty() {
    213         setUpForFullPermission();
    214         final Uri uri = insertVoicemailForSourcePackage("another-package");
    215         // Clear the mapping for our own UID so that this doesn't look like an internal transaction.
    216         mPackageManager.removePackage(Process.myUid());
    217 
    218         ContentValues values = new ContentValues();
    219         values.put(Calls.CACHED_NAME, "foo");
    220         mResolver.update(ContentUris
    221                         .withAppendedId(CallLog.Calls.CONTENT_URI, ContentUris.parseId(uri)),
    222                 values, null, null);
    223 
    224         try (Cursor cursor = mResolver
    225                 .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
    226             cursor.moveToFirst();
    227             assertEquals(cursor.getInt(0), 0);
    228         }
    229     }
    230 
    231     public void testUpdateOwnPackageVoicemail_RemovesDirtyStatus() {
    232         ContentValues values = getTestVoicemailValues();
    233         values.put(Voicemails.DIRTY, "1");
    234         final Uri uri = mResolver.insert(voicemailUri(), values);
    235         ContentValues updateValues = new ContentValues();
    236         updateValues.put(Voicemails.IS_READ, 1);
    237         mResolver.update(uri, updateValues, null, null);
    238         // At this point, the voicemail should be set back to not dirty.
    239         ContentValues newValues = getTestVoicemailValues();
    240         newValues.put(Voicemails.IS_READ, 1);
    241         newValues.put(Voicemails.DIRTY, "0");
    242         assertStoredValues(uri, newValues);
    243     }
    244 
    245     public void testUpdateOwnPackageVoicemail_retainDirtyStatus_dirty() {
    246         ContentValues values = getTestVoicemailValues();
    247         values.put(Voicemails.DIRTY, "1");
    248         final Uri uri = mResolver.insert(voicemailUri(), values);
    249 
    250         ContentValues retainDirty = new ContentValues();
    251         retainDirty.put(Voicemails.TRANSCRIPTION, "foo");
    252         retainDirty.put(Voicemails.DIRTY, Voicemails.DIRTY_RETAIN);
    253 
    254         mResolver.update(uri, retainDirty, null, null);
    255         ContentValues newValues = getTestVoicemailValues();
    256         newValues.put(Voicemails.DIRTY, "1");
    257         newValues.put(Voicemails.TRANSCRIPTION, "foo");
    258         assertStoredValues(uri, newValues);
    259     }
    260 
    261     public void testUpdateOwnPackageVoicemail_retainDirtyStatus_notDirty() {
    262         ContentValues values = getTestVoicemailValues();
    263         values.put(Voicemails.DIRTY, "0");
    264         final Uri uri = mResolver.insert(voicemailUri(), values);
    265 
    266         ContentValues retainDirty = new ContentValues();
    267         retainDirty.put(Voicemails.TRANSCRIPTION, "foo");
    268         retainDirty.put(Voicemails.DIRTY, Voicemails.DIRTY_RETAIN);
    269 
    270         mResolver.update(uri, retainDirty, null, null);
    271         ContentValues newValues = getTestVoicemailValues();
    272         newValues.put(Voicemails.DIRTY, "0");
    273         newValues.put(Voicemails.TRANSCRIPTION, "foo");
    274         assertStoredValues(uri, newValues);
    275     }
    276 
    277     public void testUpdateOwnPackageVoicemail_retainDirtyStatus_noOtherValues() {
    278         ContentValues values = getTestVoicemailValues();
    279         values.put(Voicemails.DIRTY, "1");
    280         final Uri uri = mResolver.insert(voicemailUri(), values);
    281 
    282         ContentValues retainDirty = new ContentValues();
    283         retainDirty.put(Voicemails.DIRTY, Voicemails.DIRTY_RETAIN);
    284 
    285         mResolver.update(uri, retainDirty, null, null);
    286         ContentValues newValues = getTestVoicemailValues();
    287         newValues.put(Voicemails.DIRTY, "1");
    288         assertStoredValues(uri, newValues);
    289     }
    290 
    291     public void testDeleteOwnPackageVoicemail_DeletesRow() {
    292         setUpForFullPermission();
    293         final Uri ownVoicemail = insertVoicemail();
    294         assertEquals(1, getCount(voicemailUri(), null, null));
    295 
    296         mResolver.delete(ownVoicemail, null, null);
    297 
    298         assertEquals(0, getCount(ownVoicemail, null, null));
    299     }
    300 
    301     public void testDeleteOtherPackageVoicemail_SetsDirtyStatus() {
    302         setUpForFullPermission();
    303         setTimeForTest(1000L);
    304         final Uri anotherVoicemail = insertVoicemailForSourcePackage("another-package");
    305         assertEquals(1, getCount(voicemailUri(), null, null));
    306 
    307         // Clear the mapping for our own UID so that this doesn't look like an internal transaction.
    308         mPackageManager.removePackage(Process.myUid());
    309         mResolver.delete(anotherVoicemail, null, null);
    310 
    311         ContentValues values = getTestVoicemailValues();
    312         values.put(Voicemails.DIRTY, "1");
    313         values.put(Voicemails.DELETED, "1");
    314 
    315         assertEquals(1, getCount(anotherVoicemail, null, null));
    316         assertStoredValues(anotherVoicemail, values);
    317         assertLastModified(anotherVoicemail, 1000);
    318     }
    319 
    320     public void testDelete() {
    321         Uri uri = insertVoicemail();
    322         int count = mResolver.delete(voicemailUri(), Voicemails._ID + "="
    323                 + ContentUris.parseId(uri), null);
    324         assertEquals(1, count);
    325         assertEquals(0, getCount(uri, null, null));
    326     }
    327 
    328     public void testUpdateAfterDelete_lastModifiedNotChanged() {
    329         setUpForFullPermission();
    330         setTimeForTest(1000L);
    331         final Uri anotherVoicemail = insertVoicemailForSourcePackage("another-package");
    332         assertEquals(1, getCount(voicemailUri(), null, null));
    333 
    334         // Clear the mapping for our own UID so that this doesn't look like an internal transaction.
    335         mPackageManager.removePackage(Process.myUid());
    336         mResolver.delete(anotherVoicemail, null, null);
    337         assertLastModified(anotherVoicemail, 1000);
    338 
    339         mPackageManager.addPackage(Process.myUid(), mActor.packageName);
    340         setTimeForTest(2000L);
    341         mResolver.update(anotherVoicemail, new ContentValues(), null, null);
    342         assertLastModified(anotherVoicemail, 1000);
    343 
    344         setTimeForTest(3000L);
    345         ContentValues values = new ContentValues();
    346         values.put(Voicemails.DELETED, "0");
    347         mResolver.update(anotherVoicemail, values, null, null);
    348         assertLastModified(anotherVoicemail, 3000);
    349     }
    350 
    351     public void testGetType_ItemUri() throws Exception {
    352         // Random item uri.
    353         assertEquals(Voicemails.ITEM_TYPE,
    354                 mResolver.getType(ContentUris.withAppendedId(Voicemails.CONTENT_URI, 100)));
    355         // Item uri of an inserted voicemail.
    356         ContentValues values = getTestVoicemailValues();
    357         values.put(Voicemails.MIME_TYPE, "foo/bar");
    358         Uri uri = mResolver.insert(voicemailUri(), values);
    359         assertEquals(Voicemails.ITEM_TYPE, mResolver.getType(uri));
    360     }
    361 
    362     public void testGetType_DirUri() throws Exception {
    363         assertEquals(Voicemails.DIR_TYPE, mResolver.getType(Voicemails.CONTENT_URI));
    364         assertEquals(Voicemails.DIR_TYPE, mResolver.getType(Voicemails.buildSourceUri("foo")));
    365     }
    366 
    367     // Test to ensure that without full permission it is not possible to use the base uri (i.e. with
    368     // no package URI specified).
    369     public void testMustUsePackageUriWithoutFullPermission() {
    370         setUpForOwnPermission();
    371         assertBaseUriThrowsSecurityExceptions();
    372         setUpForOwnPermissionViaCarrierPrivileges();
    373         assertBaseUriThrowsSecurityExceptions();
    374     }
    375 
    376     private void assertBaseUriThrowsSecurityExceptions() {
    377         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    378             @Override
    379             public void run() {
    380                 mResolver.insert(Voicemails.CONTENT_URI, getTestVoicemailValues());
    381             }
    382         });
    383 
    384         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    385             @Override
    386             public void run() {
    387                 mResolver.update(Voicemails.CONTENT_URI, getTestVoicemailValues(), null, null);
    388             }
    389         });
    390 
    391         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    392             @Override
    393             public void run() {
    394                 mResolver.query(Voicemails.CONTENT_URI, null, null, null, null);
    395             }
    396         });
    397 
    398         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    399             @Override
    400             public void run() {
    401                 mResolver.delete(Voicemails.CONTENT_URI, null, null);
    402             }
    403         });
    404     }
    405 
    406     public void testPermissions_InsertAndQuery() {
    407         setUpForFullPermission();
    408         // Insert two records - one each with own and another package.
    409         insertVoicemail();
    410         insertVoicemailForSourcePackage("another-package");
    411         assertEquals(2, getCount(voicemailUri(), null, null));
    412 
    413         // Now give away full permission and check that only 1 message is accessible.
    414         setUpForOwnPermission();
    415         assertOnlyOwnVoicemailsCanBeQueriedAndInserted();
    416         // Same as above, but with carrier privileges.
    417         setUpForOwnPermissionViaCarrierPrivileges();
    418         assertOnlyOwnVoicemailsCanBeQueriedAndInserted();
    419 
    420         setUpForNoPermission();
    421         mUseSourceUri = false;
    422         // With the READ_ALL_VOICEMAIL permission, we should now be able to read all voicemails
    423         mActor.addPermissions(READ_VOICEMAIL_PERMISSION);
    424         assertEquals(2, getCount(voicemailUri(), null, null));
    425 
    426         // An insert for another package should still fail
    427         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    428             @Override
    429             public void run() {
    430                 insertVoicemailForSourcePackage("another-package");
    431             }
    432         });
    433     }
    434 
    435     private void assertOnlyOwnVoicemailsCanBeQueriedAndInserted() {
    436         assertEquals(1, getCount(voicemailUri(), null, null));
    437 
    438         // Once again try to insert message for another package. This time
    439         // it should fail.
    440         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    441             @Override
    442             public void run() {
    443                 insertVoicemailForSourcePackage("another-package");
    444             }
    445         });
    446     }
    447 
    448     public void testPermissions_UpdateAndDelete() {
    449         setUpForFullPermission();
    450         // Insert two records - one each with own and another package.
    451         final Uri ownVoicemail = insertVoicemail();
    452         final Uri anotherVoicemail = insertVoicemailForSourcePackage("another-package");
    453         assertEquals(2, getCount(voicemailUri(), null, null));
    454 
    455         // Now give away full permission and check that we can update and delete only
    456         // the own voicemail.
    457         setUpForOwnPermission();
    458         assertOnlyOwnVoicemailsCanBeUpdatedAndDeleted(ownVoicemail, anotherVoicemail);
    459         setUpForOwnPermissionViaCarrierPrivileges();
    460         assertOnlyOwnVoicemailsCanBeUpdatedAndDeleted(ownVoicemail, anotherVoicemail);
    461 
    462         // If we have the manage voicemail permission, we should be able to both update voicemails
    463         // from all packages.
    464         setUpForNoPermission();
    465         mActor.addPermissions(WRITE_VOICEMAIL_PERMISSION);
    466         mResolver.update(anotherVoicemail, getTestVoicemailValues(), null, null);
    467 
    468         // Now add the read voicemail permission temporarily to verify that the update actually
    469         // worked
    470         mActor.addPermissions(READ_VOICEMAIL_PERMISSION);
    471         assertStoredValues(anotherVoicemail, getTestVoicemailValues());
    472         mActor.removePermissions(READ_VOICEMAIL_PERMISSION);
    473 
    474         mResolver.delete(anotherVoicemail, null, null);
    475 
    476         // Now add the read voicemail permission temporarily to verify that the voicemail is
    477         // deleted.
    478         mActor.addPermissions(READ_VOICEMAIL_PERMISSION);
    479 
    480         assertEquals(0, getCount(anotherVoicemail, null, null));
    481     }
    482 
    483     private void assertOnlyOwnVoicemailsCanBeUpdatedAndDeleted(
    484             Uri ownVoicemail, Uri anotherVoicemail) {
    485         mResolver.update(withSourcePackageParam(ownVoicemail),
    486                 getTestVoicemailValues(), null, null);
    487         mResolver.delete(withSourcePackageParam(ownVoicemail), null, null);
    488 
    489         // However, attempting to update or delete another-package's voicemail should fail.
    490         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    491             @Override
    492             public void run() {
    493                 mResolver.update(anotherVoicemail, null, null, null);
    494             }
    495         });
    496         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    497             @Override
    498             public void run() {
    499                 mResolver.delete(anotherVoicemail, null, null);
    500             }
    501         });
    502     }
    503 
    504     private Uri withSourcePackageParam(Uri uri) {
    505         return uri.buildUpon()
    506                 .appendQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE,
    507                         mActor.packageName)
    508                 .build();
    509     }
    510 
    511     public void testUriPermissions() {
    512         setUpForFullPermission();
    513         final Uri uri1 = insertVoicemail();
    514         final Uri uri2 = insertVoicemail();
    515         // Give away all permissions before querying. Access to both uris should be denied.
    516         setUpForNoPermission();
    517         checkHasNoAccessToUri(uri1);
    518         checkHasNoAccessToUri(uri2);
    519 
    520         // Just grant permission to uri1. uri1 should pass but uri2 should still fail.
    521         mActor.addUriPermissions(uri1);
    522         checkHasReadOnlyAccessToUri(uri1);
    523         checkHasNoAccessToUri(uri2);
    524 
    525         // Cleanup.
    526         mActor.removeUriPermissions(uri1);
    527     }
    528 
    529     /*
    530      * Checks that the READ_ALL_VOICEMAIL permission provides read access to a uri.
    531      */
    532     public void testUriPermissions_ReadAccess() {
    533         setUpForFullPermission();
    534         final Uri uri1 = insertVoicemail();
    535         // Give away all permissions before querying. Access should be denied.
    536         setUpForNoPermission();
    537         mUseSourceUri = false;
    538         checkHasNoAccessToUri(uri1);
    539 
    540         mActor.addPermissions(READ_VOICEMAIL_PERMISSION);
    541         checkHasReadAccessToUri(uri1);
    542     }
    543 
    544     /*
    545      * Checks that the MANAGE_VOICEMAIL permission provides write access to a uri.
    546      */
    547     public void testUriPermissions_WriteAccess() {
    548         setUpForFullPermission();
    549         final Uri uri1 = insertVoicemail();
    550         // Give away all permissions before querying. Access should be denied.
    551         setUpForNoPermission();
    552         checkHasNoAccessToUri(uri1);
    553 
    554         mActor.addPermissions(WRITE_VOICEMAIL_PERMISSION);
    555         checkHasUpdateAndDeleteAccessToUri(uri1);
    556     }
    557 
    558     private void checkHasNoAccessToUri(final Uri uri) {
    559         checkHasNoReadAccessToUri(uri);
    560         checkHasNoWriteAccessToUri(uri);
    561     }
    562 
    563     private void checkHasReadOnlyAccessToUri(final Uri uri) {
    564         checkHasReadAccessToUri(uri);
    565         checkHasNoWriteAccessToUri(uri);
    566     }
    567 
    568     private void checkHasReadAccessToUri(final Uri uri) {
    569         Cursor cursor = null;
    570         try {
    571             cursor = mResolver.query(uri, null, null, null, null);
    572             assertEquals(1, cursor.getCount());
    573             try {
    574                 ParcelFileDescriptor fd = mResolver.openFileDescriptor(uri, "r");
    575                 assertNotNull(fd);
    576                 fd.close();
    577             } catch (FileNotFoundException e) {
    578                 fail(e.getMessage());
    579             } catch (IOException e) {
    580                 fail(e.getMessage());
    581             }
    582         } finally {
    583             MoreCloseables.closeQuietly(cursor);
    584         }
    585     }
    586 
    587     private void checkHasNoReadAccessToUri(final Uri uri) {
    588         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    589             @Override
    590             public void run() {
    591                 mResolver.query(uri, null, null, null, null);
    592             }
    593         });
    594         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    595             @Override
    596             public void run() {
    597                 try {
    598                     mResolver.openFileDescriptor(uri, "r");
    599                 } catch (FileNotFoundException e) {
    600                     fail(e.getMessage());
    601                 }
    602             }
    603         });
    604     }
    605 
    606     private void checkHasUpdateAndDeleteAccessToUri(final Uri uri) {
    607         mResolver.update(uri, getTestVoicemailValues(), null, null);
    608         mResolver.delete(uri, null, null);
    609     }
    610 
    611     private void checkHasNoWriteAccessToUri(final Uri uri) {
    612         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    613             @Override
    614             public void run() {
    615                 mResolver.update(uri, getTestVoicemailValues(), null, null);
    616             }
    617         });
    618         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    619             @Override
    620             public void run() {
    621                 mResolver.delete(uri, null, null);
    622             }
    623         });
    624         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    625             @Override
    626             public void run() {
    627                 try {
    628                     mResolver.openFileDescriptor(uri, "w");
    629                 } catch (FileNotFoundException e) {
    630                     fail(e.getMessage());
    631                 }
    632             }
    633         });
    634         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    635             @Override
    636             public void run() {
    637                 try {
    638                     mResolver.openFileDescriptor(uri, "rw");
    639                 } catch (FileNotFoundException e) {
    640                     fail(e.getMessage());
    641                 }
    642             }
    643         });
    644     }
    645 
    646     // Test to ensure that all operations fail when no voicemail permission is granted.
    647     public void testNoPermissions() {
    648         setUpForNoPermission();
    649         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    650             @Override
    651             public void run() {
    652                 mResolver.insert(voicemailUri(), getTestVoicemailValues());
    653             }
    654         });
    655         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    656             @Override
    657             public void run() {
    658                 mResolver.update(voicemailUri(), getTestVoicemailValues(), null, null);
    659             }
    660         });
    661         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    662             @Override
    663             public void run() {
    664                 mResolver.query(voicemailUri(), null, null, null, null);
    665             }
    666         });
    667         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    668             @Override
    669             public void run() {
    670                 mResolver.delete(voicemailUri(), null, null);
    671             }
    672         });
    673     }
    674 
    675     // Test to check that none of the call_log provider specific fields are
    676     // insertable through voicemail provider.
    677     public void testCannotAccessCallLogSpecificFields_Insert() {
    678         for (String callLogColumn : CALLLOG_PROVIDER_SPECIFIC_COLUMNS) {
    679             final ContentValues values = getTestVoicemailValues();
    680             values.put(callLogColumn, "foo");
    681             EvenMoreAsserts.assertThrows("Column: " + callLogColumn,
    682                     IllegalArgumentException.class, new Runnable() {
    683                         @Override
    684                         public void run() {
    685                             mResolver.insert(voicemailUri(), values);
    686                         }
    687                     });
    688         }
    689     }
    690 
    691     // Test to check that none of the call_log provider specific fields are
    692     // exposed through voicemail provider query.
    693     public void testCannotAccessCallLogSpecificFields_Query() {
    694         // Query.
    695         Cursor cursor = mResolver.query(voicemailUri(), null, null, null, null);
    696         List<String> columnNames = Arrays.asList(cursor.getColumnNames());
    697         assertEquals(NUM_VOICEMAIL_FIELDS, columnNames.size());
    698         // None of the call_log provider specific columns should be present.
    699         for (String callLogColumn : CALLLOG_PROVIDER_SPECIFIC_COLUMNS) {
    700             assertFalse("Unexpected column: '" + callLogColumn + "' returned.",
    701                     columnNames.contains(callLogColumn));
    702         }
    703     }
    704 
    705     // Test to check that none of the call_log provider specific fields are
    706     // updatable through voicemail provider.
    707     public void testCannotAccessCallLogSpecificFields_Update() {
    708         for (String callLogColumn : CALLLOG_PROVIDER_SPECIFIC_COLUMNS) {
    709             final Uri insertedUri = insertVoicemail();
    710             final ContentValues values = getTestVoicemailValues();
    711             values.put(callLogColumn, "foo");
    712             EvenMoreAsserts.assertThrows("Column: " + callLogColumn,
    713                     IllegalArgumentException.class, new Runnable() {
    714                         @Override
    715                         public void run() {
    716                             mResolver.update(insertedUri, values, null, null);
    717                         }
    718                     });
    719         }
    720     }
    721 
    722     // Tests for voicemail status table.
    723 
    724     public void testStatusInsert() throws Exception {
    725         ContentValues values = getTestStatusValues();
    726         Uri uri = mResolver.insert(statusUri(), values);
    727         assertStoredValues(uri, values);
    728         assertSelection(uri, values, Status._ID, ContentUris.parseId(uri));
    729     }
    730 
    731     public void testStatusUpdate() throws Exception {
    732         Uri uri = insertTestStatusEntry();
    733         ContentValues values = getTestStatusValues();
    734         values.put(Status.DATA_CHANNEL_STATE, Status.DATA_CHANNEL_STATE_NO_CONNECTION);
    735         values.put(Status.NOTIFICATION_CHANNEL_STATE,
    736                 Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING);
    737         values.put(Status.SOURCE_TYPE,
    738                 "vvm_type_test2");
    739         int count = mResolver.update(uri, values, null, null);
    740         assertEquals(1, count);
    741         assertStoredValues(uri, values);
    742     }
    743 
    744     public void testStatusUpdate_observerNotified() throws Exception {
    745         Uri uri = insertTestStatusEntry();
    746         ContentValues values = getTestStatusValues();
    747         values.put(Status.DATA_CHANNEL_STATE, Status.DATA_CHANNEL_STATE_NO_CONNECTION);
    748         values.put(Status.NOTIFICATION_CHANNEL_STATE,
    749             Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING);
    750         values.put(Status.SOURCE_TYPE,
    751             "vvm_type_test2");
    752         Boolean[] observerTriggered = new Boolean[]{false};
    753         mResolver.registerContentObserver(Status.CONTENT_URI, true,
    754             new ContentObserver(new Handler()) {
    755                 @Override
    756                 public void onChange(boolean selfChange, Uri uri) {
    757                     observerTriggered[0] = true;
    758                 }
    759             });
    760 
    761         mResolver.update(uri, values, null, null);
    762 
    763         assertTrue(observerTriggered[0]);
    764     }
    765 
    766     public void testStatusUpsert() throws Exception {
    767         ContentValues values = getTestStatusValues();
    768         mResolver.insert(statusUri(), values);
    769         ContentValues values2 = new ContentValues();
    770         values2.put(Status.CONFIGURATION_STATE, Status.CONFIGURATION_STATE_NOT_CONFIGURED);
    771         values.put(Status.CONFIGURATION_STATE, Status.CONFIGURATION_STATE_NOT_CONFIGURED);
    772         Uri uri = mResolver.insert(statusUri(), values2);
    773         assertStoredValues(uri, values);
    774         assertSelection(uri, values, Status._ID, ContentUris.parseId(uri));
    775     }
    776 
    777     public void testStatusDelete() {
    778         Uri uri = insertTestStatusEntry();
    779         int count = mResolver.delete(statusUri(), Status._ID + "="
    780                 + ContentUris.parseId(uri), null);
    781         assertEquals(1, count);
    782         assertEquals(0, getCount(uri, null, null));
    783     }
    784 
    785     public void testStatusQuotaInsert() {
    786         ContentValues values = new ContentValues();
    787         values.put(Status.SOURCE_PACKAGE, mActor.packageName);
    788         values.put(Status.QUOTA_OCCUPIED, 2);
    789         values.put(Status.QUOTA_TOTAL, 13);
    790         Uri uri = mResolver.insert(statusUri(), values);
    791         assertStoredValues(uri, values);
    792         assertSelection(uri, values, Status._ID, ContentUris.parseId(uri));
    793     }
    794 
    795     public void testStatusQuotaUpdate() {
    796         Uri uri = insertTestStatusEntry();
    797         ContentValues values = new ContentValues();
    798         values.put(Status.SOURCE_PACKAGE, mActor.packageName);
    799         values.put(Status.QUOTA_OCCUPIED, 2);
    800         values.put(Status.QUOTA_TOTAL, 13);
    801         int count = mResolver.update(uri, values, null, null);
    802         assertEquals(1, count);
    803 
    804         ContentValues refValues = getTestStatusValues();
    805         refValues.put(Status.QUOTA_OCCUPIED, 2);
    806         refValues.put(Status.QUOTA_TOTAL, 13);
    807         assertStoredValues(uri, refValues);
    808     }
    809 
    810     public void testStatusQuotaUpsert() {
    811         Uri uri = insertTestStatusEntry();
    812         ContentValues values = new ContentValues();
    813         values.put(Status.SOURCE_PACKAGE, mActor.packageName);
    814         values.put(Status.QUOTA_OCCUPIED, 2);
    815         int count = mResolver.update(uri, values, null, null);
    816 
    817         ContentValues values2 = new ContentValues();
    818         values2.put(Status.QUOTA_TOTAL, 13);
    819         mResolver.insert(uri, values2);
    820 
    821         ContentValues refValues = getTestStatusValues();
    822         refValues.put(Status.QUOTA_OCCUPIED, 2);
    823         refValues.put(Status.QUOTA_TOTAL, 13);
    824         assertStoredValues(uri, refValues);
    825     }
    826 
    827     public void testStatusGetType() throws Exception {
    828         // Item URI.
    829         Uri uri = insertTestStatusEntry();
    830         assertEquals(Status.ITEM_TYPE, mResolver.getType(uri));
    831 
    832         // base URIs.
    833         assertEquals(Status.DIR_TYPE, mResolver.getType(Status.CONTENT_URI));
    834         assertEquals(Status.DIR_TYPE, mResolver.getType(Status.buildSourceUri("foo")));
    835     }
    836 
    837     // Basic permission checks for the status table.
    838     public void testStatusPermissions() throws Exception {
    839         final ContentValues values = getTestStatusValues();
    840         // Inserting for another package should fail with any of the URIs.
    841         values.put(Status.SOURCE_PACKAGE, "another.package");
    842         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    843             @Override
    844             public void run() {
    845                 mResolver.insert(Status.CONTENT_URI, values);
    846             }
    847         });
    848         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    849             @Override
    850             public void run() {
    851                 mResolver.insert(Status.buildSourceUri(mActor.packageName), values);
    852             }
    853         });
    854 
    855         // But insertion with own package should succeed with the right uri.
    856         values.put(Status.SOURCE_PACKAGE, mActor.packageName);
    857         final Uri uri = mResolver.insert(Status.buildSourceUri(mActor.packageName), values);
    858         assertNotNull(uri);
    859 
    860         // Updating source_package should not work as well.
    861         values.put(Status.SOURCE_PACKAGE, "another.package");
    862         EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
    863             @Override
    864             public void run() {
    865                 mResolver.update(uri, values, null, null);
    866             }
    867         });
    868     }
    869 
    870     // File operation is not supported by /status URI.
    871     public void testStatusFileOperation() throws Exception {
    872         final Uri uri = insertTestStatusEntry();
    873         EvenMoreAsserts.assertThrows(UnsupportedOperationException.class, new Runnable() {
    874             @Override
    875             public void run() {
    876                 try {
    877                     mResolver.openOutputStream(uri);
    878                 } catch (FileNotFoundException e) {
    879                     fail("Unexpected exception " + e);
    880                 }
    881             }
    882         });
    883 
    884         EvenMoreAsserts.assertThrows(UnsupportedOperationException.class, new Runnable() {
    885             @Override
    886             public void run() {
    887                 try {
    888                     mResolver.openInputStream(uri);
    889                 } catch (FileNotFoundException e) {
    890                     fail("Unexpected exception " + e);
    891                 }
    892             }
    893         });
    894     }
    895 
    896     /**
    897      * Inserts a voicemail record with no source package set. The content provider
    898      * will detect source package.
    899      */
    900     private Uri insertVoicemail() {
    901         return mResolver.insert(voicemailUri(), getTestVoicemailValues());
    902     }
    903 
    904     /**
    905      * Inserts a voicemail record for the specified source package.
    906      */
    907     private Uri insertVoicemailForSourcePackage(String sourcePackage) {
    908         ContentValues values = getTestVoicemailValues();
    909         values.put(Voicemails.SOURCE_PACKAGE, sourcePackage);
    910         return mResolver.insert(voicemailUri(), values);
    911     }
    912 
    913     private ContentValues getTestVoicemailValues() {
    914         ContentValues values = new ContentValues();
    915         values.put(Voicemails.NUMBER, "1-800-4664-411");
    916         values.put(Voicemails.DATE, 1000);
    917         values.put(Voicemails.DURATION, 30);
    918         values.put(Voicemails.NEW, 0);
    919         values.put(Voicemails.TRANSCRIPTION, "Testing 123");
    920         values.put(Voicemails.IS_READ, 0);
    921         values.put(Voicemails.HAS_CONTENT, 0);
    922         values.put(Voicemails.SOURCE_DATA, "1234");
    923         values.put(Voicemails.STATE, Voicemails.STATE_INBOX);
    924         values.put(Voicemails.BACKED_UP, 0);
    925         values.put(Voicemails.RESTORED, 0);
    926         values.put(Voicemails.ARCHIVED, 0);
    927         values.put(Voicemails.IS_OMTP_VOICEMAIL, 0);
    928         return values;
    929     }
    930 
    931     private ContentValues getTestReadVoicemailValues() {
    932         ContentValues values = getTestVoicemailValues();
    933         values.put(Voicemails.IS_READ, 1);
    934         return values;
    935     }
    936 
    937     private Uri insertTestStatusEntry() {
    938         return mResolver.insert(statusUri(), getTestStatusValues());
    939     }
    940 
    941     private ContentValues getTestStatusValues() {
    942         ContentValues values = new ContentValues();
    943         values.put(Status.SOURCE_PACKAGE, mActor.packageName);
    944         values.put(Status.VOICEMAIL_ACCESS_URI, "tel:901");
    945         values.put(Status.SETTINGS_URI, "com.example.voicemail.source.SettingsActivity");
    946         values.put(Status.CONFIGURATION_STATE, Status.CONFIGURATION_STATE_OK);
    947         values.put(Status.DATA_CHANNEL_STATE, Status.DATA_CHANNEL_STATE_OK);
    948         values.put(Status.NOTIFICATION_CHANNEL_STATE, Status.NOTIFICATION_CHANNEL_STATE_OK);
    949         values.put(Status.SOURCE_TYPE, "vvm_type_test");
    950         return values;
    951     }
    952 
    953 }
    954