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