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 android.provider.cts.contacts;
18 
19 import android.app.Instrumentation;
20 import android.content.ContentProviderClient;
21 import android.content.ContentResolver;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.content.pm.PackageManager;
25 import android.database.Cursor;
26 import android.net.Uri;
27 import android.os.ParcelFileDescriptor;
28 import android.os.RemoteException;
29 import android.provider.VoicemailContract;
30 import android.provider.VoicemailContract.Status;
31 import android.provider.VoicemailContract.Voicemails;
32 import android.test.InstrumentationTestCase;
33 import android.text.TextUtils;
34 import android.util.Log;
35 
36 import java.io.BufferedReader;
37 import java.io.FileInputStream;
38 import java.io.InputStream;
39 import java.io.InputStreamReader;
40 import java.nio.charset.StandardCharsets;
41 
42 /**
43  * CTS tests for voicemail provider accessed through {@link VoicemailContract}.
44  */
45 public class VoicemailContractTest extends InstrumentationTestCase {
46 
47     private static final String TAG = "VoicemailContractTest";
48 
49     private ContentResolver mContentResolver;
50     private ContentProviderClient mVoicemailProvider;
51     private ContentProviderClient mStatusProvider;
52     private Uri mVoicemailContentUri;
53     private Uri mStatusContentUri;
54     private String mSourcePackageName;
55 
56     private String mPreviousDefaultDialer;
57 
58     private static final String COMMAND_SET_DEFAULT_DIALER = "telecom set-default-dialer ";
59     private static final String COMMAND_GET_DEFAULT_DIALER = "telecom get-default-dialer";
60 
61     private static final String PACKAGE = "android.provider.cts";
62 
63     private final String FOREIGN_SOURCE = "android.provider.cts.contacts.foreign_source";
64 
65     @Override
setUp()66     protected void setUp() throws Exception {
67         super.setUp();
68         mSourcePackageName = getInstrumentation().getTargetContext().getPackageName();
69         mVoicemailContentUri = Voicemails.buildSourceUri(mSourcePackageName);
70         mStatusContentUri = Status.buildSourceUri(mSourcePackageName);
71         mContentResolver = getInstrumentation().getTargetContext().getContentResolver();
72         mVoicemailProvider = mContentResolver.acquireContentProviderClient(mVoicemailContentUri);
73         mStatusProvider = mContentResolver.acquireContentProviderClient(mStatusContentUri);
74     }
75 
76     @Override
tearDown()77     protected void tearDown() throws Exception {
78         // Clean up, just in case we failed to delete the entry when a test failed.
79         // The cotentUris are specific to this package, so this will delete only the
80         // entries inserted by this package.
81         mStatusProvider.delete(mStatusContentUri, null, null);
82         mVoicemailProvider.delete(mVoicemailContentUri, null, null);
83         if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
84             setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
85         }
86         super.tearDown();
87     }
88 
testVoicemailsTable()89     public void testVoicemailsTable() throws Exception {
90         final String[] VOICEMAILS_PROJECTION = new String[] {
91                 Voicemails._ID,
92                 Voicemails.NUMBER,
93                 Voicemails.DATE,
94                 Voicemails.DURATION,
95                 Voicemails.NEW,
96                 Voicemails.IS_READ,
97                 Voicemails.SOURCE_PACKAGE,
98                 Voicemails.SOURCE_DATA,
99                 Voicemails.HAS_CONTENT,
100                 Voicemails.MIME_TYPE,
101                 Voicemails.TRANSCRIPTION,
102                 Voicemails.PHONE_ACCOUNT_COMPONENT_NAME,
103                 Voicemails.PHONE_ACCOUNT_ID,
104                 Voicemails.DIRTY,
105                 Voicemails.DELETED,
106                 Voicemails.LAST_MODIFIED,
107                 Voicemails.BACKED_UP,
108                 Voicemails.RESTORED,
109                 Voicemails.ARCHIVED,
110                 Voicemails.IS_OMTP_VOICEMAIL};
111         final int ID_INDEX = 0;
112         final int NUMBER_INDEX = 1;
113         final int DATE_INDEX = 2;
114         final int DURATION_INDEX = 3;
115         final int NEW_INDEX = 4;
116         final int IS_READ_INDEX = 5;
117         final int SOURCE_PACKAGE_INDEX = 6;
118         final int SOURCE_DATA_INDEX = 7;
119         final int HAS_CONTENT_INDEX = 8;
120         final int MIME_TYPE_INDEX = 9;
121         final int TRANSCRIPTION_INDEX = 10;
122         final int PHONE_ACCOUNT_COMPONENT_NAME_INDEX = 11;
123         final int PHONE_ACCOUNT_ID_INDEX = 12;
124         final int DIRTY_INDEX = 13;
125         final int DELETED_INDEX = 14;
126         final int LAST_MODIFIED_INDEX = 15;
127         final int BACKED_UP_INDEX = 16;
128         final int RESTORED_INDEX = 17;
129         final int ARCHIVED_INDEX = 18;
130         final int IS_OMTP_VOICEMAIL_INDEX = 19;
131 
132         String insertCallsNumber = "0123456789";
133         long insertCallsDuration = 120;
134         String insertSourceData = "internal_id";
135         String insertMimeType = "audio/mp3";
136         long insertDate = 1324478862000L;
137 
138         String updateCallsNumber = "9876543210";
139         long updateCallsDuration = 310;
140         String updateSourceData = "another_id";
141         long updateDate = 1324565262000L;
142 
143         // Test: insert
144         ContentValues value = new ContentValues();
145         value.put(Voicemails.NUMBER, insertCallsNumber);
146         value.put(Voicemails.DATE, insertDate);
147         value.put(Voicemails.DURATION, insertCallsDuration);
148         value.put(Voicemails.NEW, 0);
149         // Source package is expected to be inserted by the provider, if not set.
150         value.put(Voicemails.SOURCE_DATA, insertSourceData);
151         value.put(Voicemails.MIME_TYPE, insertMimeType);
152         value.put(Voicemails.IS_READ, false);
153         value.put(Voicemails.HAS_CONTENT, true);
154         value.put(Voicemails.TRANSCRIPTION, "foo");
155         value.put(Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, "com.foo");
156         value.put(Voicemails.PHONE_ACCOUNT_ID, "bar");
157         value.put(Voicemails.DIRTY, 0);
158         value.put(Voicemails.DELETED, 0);
159         value.put(Voicemails.BACKED_UP, 0);
160         value.put(Voicemails.RESTORED, 0);
161         value.put(Voicemails.ARCHIVED, 0);
162         value.put(Voicemails.IS_OMTP_VOICEMAIL, 0);
163 
164         Uri uri = mVoicemailProvider.insert(mVoicemailContentUri, value);
165         Cursor cursor = mVoicemailProvider.query(
166                 mVoicemailContentUri, VOICEMAILS_PROJECTION,
167                 Voicemails.NUMBER + " = ?",
168                 new String[] {insertCallsNumber}, null, null);
169         assertTrue(cursor.moveToNext());
170         assertEquals(insertCallsNumber, cursor.getString(NUMBER_INDEX));
171         assertEquals(insertDate, cursor.getLong(DATE_INDEX));
172         assertEquals(insertCallsDuration, cursor.getLong(DURATION_INDEX));
173         assertEquals(mSourcePackageName, cursor.getString(SOURCE_PACKAGE_INDEX));
174         assertEquals(insertSourceData, cursor.getString(SOURCE_DATA_INDEX));
175         assertEquals(insertMimeType, cursor.getString(MIME_TYPE_INDEX));
176         assertEquals(0, cursor.getInt(NEW_INDEX));
177         assertEquals(0, cursor.getInt(IS_READ_INDEX));
178         assertEquals(1, cursor.getInt(HAS_CONTENT_INDEX));
179         assertEquals("foo", cursor.getString(TRANSCRIPTION_INDEX));
180         assertEquals("com.foo", cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME_INDEX));
181         assertEquals("bar", cursor.getString(PHONE_ACCOUNT_ID_INDEX));
182         assertEquals(0, cursor.getInt(DIRTY_INDEX));
183         assertEquals(0, cursor.getInt(DELETED_INDEX));
184         assertEquals(0, cursor.getInt(BACKED_UP_INDEX));
185         assertEquals(0, cursor.getInt(RESTORED_INDEX));
186         assertEquals(0, cursor.getInt(ARCHIVED_INDEX));
187         assertEquals(0, cursor.getInt(IS_OMTP_VOICEMAIL_INDEX));
188         int id = cursor.getInt(ID_INDEX);
189         assertEquals(id, Integer.parseInt(uri.getLastPathSegment()));
190         cursor.close();
191 
192         // Test: update
193         value.clear();
194         value.put(Voicemails.NUMBER, updateCallsNumber);
195         value.put(Voicemails.DATE, updateDate);
196         value.put(Voicemails.DURATION, updateCallsDuration);
197         value.put(Voicemails.SOURCE_DATA, updateSourceData);
198         value.put(Voicemails.NEW, 1);
199         value.put(Voicemails.DIRTY, 1);
200         value.put(Voicemails.DELETED, 1);
201         value.put(Voicemails.BACKED_UP, 1);
202         value.put(Voicemails.RESTORED, 1);
203         value.put(Voicemails.ARCHIVED, 1);
204         value.put(Voicemails.IS_OMTP_VOICEMAIL, 1);
205 
206         mVoicemailProvider.update(uri, value, null, null);
207         cursor = mVoicemailProvider.query(mVoicemailContentUri, VOICEMAILS_PROJECTION,
208                 Voicemails._ID + " = " + id, null, null, null);
209         assertEquals(1, cursor.getCount());
210         assertTrue(cursor.moveToNext());
211         assertEquals(mSourcePackageName, cursor.getString(SOURCE_PACKAGE_INDEX));
212         assertEquals(updateCallsNumber, cursor.getString(NUMBER_INDEX));
213         assertEquals(updateDate, cursor.getLong(DATE_INDEX));
214         assertEquals(updateCallsDuration, cursor.getLong(DURATION_INDEX));
215         assertEquals(updateSourceData, cursor.getString(SOURCE_DATA_INDEX));
216         assertEquals(1, cursor.getInt(NEW_INDEX));
217         assertEquals(1, cursor.getInt(DIRTY_INDEX));
218         assertEquals(1, cursor.getInt(DELETED_INDEX));
219         assertEquals(1, cursor.getInt(BACKED_UP_INDEX));
220         assertEquals(1, cursor.getInt(RESTORED_INDEX));
221         assertEquals(1, cursor.getInt(ARCHIVED_INDEX));
222         assertEquals(1, cursor.getInt(IS_OMTP_VOICEMAIL_INDEX));
223         cursor.close();
224 
225         // Test: delete
226         mVoicemailProvider.delete(mVoicemailContentUri, Voicemails._ID + " = " + id, null);
227         cursor = mVoicemailProvider.query(mVoicemailContentUri, VOICEMAILS_PROJECTION,
228                 Voicemails._ID + " = " + id, null, null, null);
229         assertEquals(0, cursor.getCount());
230         cursor.close();
231     }
232 
testForeignUpdate_dirty()233     public void testForeignUpdate_dirty() throws Exception {
234         if (!hasTelephony(getInstrumentation().getContext())) {
235             Log.d(TAG, "skipping test that requires telephony feature");
236             return;
237         }
238         // only the default dialer has WRITE_VOICEMAIL permission, which can modify voicemails of
239         // a foreign source package.
240         setTestAsDefaultDialer();
241         ContentValues values = new ContentValues();
242         values.put(Voicemails.SOURCE_PACKAGE, FOREIGN_SOURCE);
243 
244         Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
245 
246         ContentValues updateValues = new ContentValues();
247         updateValues.put(Voicemails.IS_READ, "1");
248         mVoicemailProvider.update(uri, updateValues, null, null);
249 
250         try (Cursor cursor = mVoicemailProvider
251                 .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
252             cursor.moveToFirst();
253             assertEquals(1, cursor.getInt(0));
254         }
255     }
256 
testForeignUpdate_retainDirty_notDirty()257     public void testForeignUpdate_retainDirty_notDirty() throws Exception {
258         if (!hasTelephony(getInstrumentation().getContext())) {
259             Log.d(TAG, "skipping test that requires telephony feature");
260             return;
261         }
262         // only the default dialer has WRITE_VOICEMAIL permission, which can modify voicemails of
263         // a foreign source package.
264         setTestAsDefaultDialer();
265         ContentValues values = new ContentValues();
266         values.put(Voicemails.SOURCE_PACKAGE, FOREIGN_SOURCE);
267 
268         Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
269 
270         ContentValues newValues = new ContentValues();
271         newValues.put(Voicemails.TRANSCRIPTION, "foo");
272         newValues.put(Voicemails.DIRTY, Voicemails.DIRTY_RETAIN);
273 
274         mVoicemailProvider.update(uri, newValues, null, null);
275 
276         try (Cursor cursor = mVoicemailProvider
277                 .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
278             cursor.moveToFirst();
279             assertEquals(0, cursor.getInt(0));
280         }
281     }
282 
testForeignUpdate_explicitNotDirty()283     public void testForeignUpdate_explicitNotDirty() throws Exception {
284         if (!hasTelephony(getInstrumentation().getContext())) {
285             Log.d(TAG, "skipping test that requires telephony feature");
286             return;
287         }
288         setTestAsDefaultDialer();
289         ContentValues values = new ContentValues();
290         values.put(Voicemails.SOURCE_PACKAGE, FOREIGN_SOURCE);
291 
292         Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
293 
294         ContentValues updateValues = new ContentValues();
295         updateValues.put(Voicemails.DIRTY, 0);
296         mVoicemailProvider.update(uri, updateValues, null, null);
297 
298         try (Cursor cursor = mVoicemailProvider
299                 .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
300             cursor.moveToFirst();
301             assertEquals(0, cursor.getInt(0));
302         }
303     }
304 
testForeignUpdate_null_dirty()305     public void testForeignUpdate_null_dirty() throws Exception {
306         if (!hasTelephony(getInstrumentation().getContext())) {
307             Log.d(TAG, "skipping test that requires telephony feature");
308             return;
309         }
310         setTestAsDefaultDialer();
311         ContentValues values = new ContentValues();
312         values.put(Voicemails.SOURCE_PACKAGE, FOREIGN_SOURCE);
313 
314         Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
315 
316         ContentValues updateValues = new ContentValues();
317         updateValues.put(Voicemails.DIRTY, (Integer) null);
318         mVoicemailProvider.update(uri, updateValues, null, null);
319 
320         try (Cursor cursor = mVoicemailProvider
321                 .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
322             cursor.moveToFirst();
323             assertEquals(1, cursor.getInt(0));
324         }
325     }
326 
testForeignUpdate_NotNormalized_normalized()327     public void testForeignUpdate_NotNormalized_normalized() throws Exception {
328         if (!hasTelephony(getInstrumentation().getContext())) {
329             Log.d(TAG, "skipping test that requires telephony feature");
330             return;
331         }
332         setTestAsDefaultDialer();
333         ContentValues values = new ContentValues();
334         values.put(Voicemails.SOURCE_PACKAGE, FOREIGN_SOURCE);
335 
336         Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
337 
338         ContentValues updateValues = new ContentValues();
339         updateValues.put(Voicemails.DIRTY, 2);
340         mVoicemailProvider.update(uri, updateValues, null, null);
341 
342         try (Cursor cursor = mVoicemailProvider
343                 .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
344             cursor.moveToFirst();
345             assertEquals(1, cursor.getInt(0));
346         }
347     }
348 
testLocalUpdate_notDirty()349     public void testLocalUpdate_notDirty() throws Exception {
350 
351         ContentValues values = new ContentValues();
352         values.put(Voicemails.DIRTY, 1);
353 
354         Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(mSourcePackageName), values);
355 
356         ContentValues updateValues = new ContentValues();
357         updateValues.put(Voicemails.IS_READ, "1");
358         mVoicemailProvider.update(uri, updateValues, null, null);
359 
360         try (Cursor cursor = mVoicemailProvider
361                 .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
362             cursor.moveToFirst();
363             assertEquals(cursor.getInt(0), 0);
364         }
365     }
366 
testLocalUpdate_retainDirty_dirty()367     public void testLocalUpdate_retainDirty_dirty() throws Exception {
368 
369         ContentValues values = new ContentValues();
370         values.put(Voicemails.DIRTY, 1);
371 
372         Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(mSourcePackageName), values);
373 
374         ContentValues newValues = new ContentValues();
375         newValues.put(Voicemails.TRANSCRIPTION, "foo");
376         newValues.put(Voicemails.DIRTY, Voicemails.DIRTY_RETAIN);
377 
378         mVoicemailProvider.update(uri, newValues, null, null);
379 
380         try (Cursor cursor = mVoicemailProvider
381                 .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
382             cursor.moveToFirst();
383             assertEquals(cursor.getInt(0), 1);
384         }
385     }
386 
387     // Data column should be automatically generated during insert.
testInsert_doesNotUpdateDataColumn()388     public void testInsert_doesNotUpdateDataColumn() throws Exception {
389 
390         final String newFilePath = "my/new/file/path";
391         final ContentValues value = buildContentValuesForNewVoicemail();
392         value.put(Voicemails._DATA, newFilePath);
393         mVoicemailProvider.insert(mVoicemailContentUri, value);
394 
395         assertDataNotEquals(newFilePath);
396     }
397 
testDataColumnUpdate_throwsIllegalArgumentException()398     public void testDataColumnUpdate_throwsIllegalArgumentException() throws Exception {
399 
400         final ContentValues value = buildContentValuesForNewVoicemail();
401         final Uri uri = mVoicemailProvider.insert(mVoicemailContentUri, value);
402 
403         // Test: update
404         final String newFilePath = "another/file/path";
405 
406         value.clear();
407         value.put(Voicemails._DATA, newFilePath);
408         try {
409             mVoicemailProvider.update(uri, value, null, null);
410             fail("IllegalArgumentException expected but not thrown.");
411         } catch (IllegalArgumentException e) {
412             // pass
413         }
414 
415         assertDataNotEquals(newFilePath);
416     }
417 
assertDataNotEquals(String newFilePath)418     private void assertDataNotEquals(String newFilePath) throws RemoteException {
419         // Make sure data value is not actually updated.
420         final Cursor cursor = mVoicemailProvider.query(mVoicemailContentUri,
421                 new String[] {Voicemails._DATA}, null, null, null);
422         cursor.moveToNext();
423         final String data = cursor.getString(0);
424         assertFalse(data.equals(newFilePath));
425     }
426 
buildContentValuesForNewVoicemail()427     private ContentValues buildContentValuesForNewVoicemail() {
428         final String insertCallsNumber = "0123456789";
429         final long insertCallsDuration = 120;
430         final String insertSourceData = "internal_id";
431         final String insertMimeType = "audio/mp3";
432         final long insertDate = 1324478862000L;
433 
434         ContentValues value = new ContentValues();
435         value.put(Voicemails.NUMBER, insertCallsNumber);
436         value.put(Voicemails.DATE, insertDate);
437         value.put(Voicemails.DURATION, insertCallsDuration);
438         // Source package is expected to be inserted by the provider, if not set.
439         value.put(Voicemails.SOURCE_DATA, insertSourceData);
440         value.put(Voicemails.MIME_TYPE, insertMimeType);
441         value.put(Voicemails.IS_READ, false);
442         value.put(Voicemails.HAS_CONTENT, true);
443 
444         return value;
445     }
446 
testStatusTable()447     public void testStatusTable() throws Exception {
448         final String[] STATUS_PROJECTION = new String[] {
449                 Status._ID, Status.SOURCE_PACKAGE, Status.CONFIGURATION_STATE,
450                 Status.DATA_CHANNEL_STATE, Status.NOTIFICATION_CHANNEL_STATE,
451                 Status.SETTINGS_URI, Status.VOICEMAIL_ACCESS_URI,
452                 Status.QUOTA_OCCUPIED, Status.QUOTA_TOTAL};
453         final int ID_INDEX = 0;
454         final int SOURCE_PACKAGE_INDEX = 1;
455         final int CONFIGURATION_STATE_INDEX = 2;
456         final int DATA_CHANNEL_STATE_INDEX = 3;
457         final int NOTIFICATION_CHANNEL_STATE_INDEX = 4;
458         final int SETTINGS_URI_INDEX = 5;
459         final int VOICEMAIL_ACCESS_URI_INDEX = 6;
460         final int QUOTA_OCCUPIED_INDEX = 7;
461         final int QUOTA_TOTAL_INDEX = 8;
462 
463         int insertConfigurationState = Status.CONFIGURATION_STATE_OK;
464         int insertDataChannelState = Status.DATA_CHANNEL_STATE_OK;
465         int insertNotificationChannelState = Status.NOTIFICATION_CHANNEL_STATE_OK;
466         String insertSettingsUri = "settings_uri";
467         String insertVoicemailAccessUri = "tel:901";
468         int quotaOccupied = 7;
469         int quotaTotal = 42;
470 
471         int updateDataChannelState = Status.DATA_CHANNEL_STATE_NO_CONNECTION;
472         int updateNotificationChannelState = Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING;
473         String updateSettingsUri = "settings_uri_2";
474         int updateQuotaOccupied = 1337;
475         int updateQuotaTotal = 2187;
476 
477         // Test: insert
478         ContentValues value = new ContentValues();
479         value.put(Status.CONFIGURATION_STATE, insertConfigurationState);
480         value.put(Status.DATA_CHANNEL_STATE, insertDataChannelState);
481         value.put(Status.NOTIFICATION_CHANNEL_STATE, insertNotificationChannelState);
482         value.put(Status.SETTINGS_URI, insertSettingsUri);
483         value.put(Status.VOICEMAIL_ACCESS_URI, insertVoicemailAccessUri);
484         value.put(Status.QUOTA_OCCUPIED, quotaOccupied);
485         value.put(Status.QUOTA_TOTAL, quotaTotal);
486 
487         Uri uri = mStatusProvider.insert(mStatusContentUri, value);
488         Cursor cursor = mStatusProvider.query(
489                 mStatusContentUri, STATUS_PROJECTION, null, null, null, null);
490         assertTrue(cursor.moveToNext());
491         assertEquals(mSourcePackageName, cursor.getString(SOURCE_PACKAGE_INDEX));
492         assertEquals(insertConfigurationState, cursor.getInt(CONFIGURATION_STATE_INDEX));
493         assertEquals(insertDataChannelState, cursor.getInt(DATA_CHANNEL_STATE_INDEX));
494         assertEquals(insertNotificationChannelState,
495                 cursor.getInt(NOTIFICATION_CHANNEL_STATE_INDEX));
496         assertEquals(insertSettingsUri, cursor.getString(SETTINGS_URI_INDEX));
497         assertEquals(insertVoicemailAccessUri, cursor.getString(VOICEMAIL_ACCESS_URI_INDEX));
498         assertEquals(quotaOccupied, cursor.getInt(QUOTA_OCCUPIED_INDEX));
499         assertEquals(quotaTotal, cursor.getInt(QUOTA_TOTAL_INDEX));
500         int id = cursor.getInt(ID_INDEX);
501         assertEquals(id, Integer.parseInt(uri.getLastPathSegment()));
502         cursor.close();
503 
504         // Test: update
505         value.clear();
506         value.put(Status.DATA_CHANNEL_STATE, updateDataChannelState);
507         value.put(Status.NOTIFICATION_CHANNEL_STATE, updateNotificationChannelState);
508         value.put(Status.SETTINGS_URI, updateSettingsUri);
509         value.put(Status.QUOTA_OCCUPIED, updateQuotaOccupied);
510         value.put(Status.QUOTA_TOTAL, updateQuotaTotal);
511 
512         mStatusProvider.update(uri, value, null, null);
513         cursor = mStatusProvider.query(mStatusContentUri, STATUS_PROJECTION,
514                 Voicemails._ID + " = " + id, null, null, null);
515         assertEquals(1, cursor.getCount());
516         assertTrue(cursor.moveToNext());
517         assertEquals(mSourcePackageName, cursor.getString(SOURCE_PACKAGE_INDEX));
518         assertEquals(updateDataChannelState, cursor.getInt(DATA_CHANNEL_STATE_INDEX));
519         assertEquals(updateNotificationChannelState,
520                 cursor.getInt(NOTIFICATION_CHANNEL_STATE_INDEX));
521         assertEquals(updateSettingsUri, cursor.getString(SETTINGS_URI_INDEX));
522         assertEquals(updateQuotaOccupied, cursor.getInt(QUOTA_OCCUPIED_INDEX));
523         assertEquals(updateQuotaTotal, cursor.getInt(QUOTA_TOTAL_INDEX));
524         cursor.close();
525 
526         // Test: delete
527         mStatusProvider.delete(mStatusContentUri, Voicemails._ID + " = " + id, null);
528         cursor = mStatusProvider.query(mStatusContentUri, STATUS_PROJECTION,
529                 Voicemails._ID + " = " + id, null, null, null);
530         assertEquals(0, cursor.getCount());
531         cursor.close();
532     }
533 
testVoicemailTablePermissions()534     public void testVoicemailTablePermissions() throws Exception {
535         ContentValues value = new ContentValues();
536         value.put(Voicemails.NUMBER, "0123456789");
537         value.put(Voicemails.SOURCE_PACKAGE, "some.other.package");
538         try {
539             mVoicemailProvider.insert(mVoicemailContentUri, value);
540             fail("Expected SecurityException. None thrown.");
541         } catch (SecurityException e) {
542             // Expected result.
543         }
544     }
545 
testStatusTablePermissions()546     public void testStatusTablePermissions() throws Exception {
547         ContentValues value = new ContentValues();
548         value.put(Status.CONFIGURATION_STATE, Status.CONFIGURATION_STATE_OK);
549         value.put(Status.SOURCE_PACKAGE, "some.other.package");
550         try {
551             mStatusProvider.insert(mStatusContentUri, value);
552             fail("Expected SecurityException. None thrown.");
553         } catch (SecurityException e) {
554             // Expected result.
555         }
556     }
557 
hasTelephony(Context context)558     private static boolean hasTelephony(Context context) {
559         final PackageManager packageManager = context.getPackageManager();
560         return packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) &&
561                 packageManager.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE);
562     }
563 
setTestAsDefaultDialer()564     private void setTestAsDefaultDialer() throws Exception {
565         assertTrue(mPreviousDefaultDialer == null);
566         mPreviousDefaultDialer = getDefaultDialer(getInstrumentation());
567         setDefaultDialer(getInstrumentation(), PACKAGE);
568     }
569 
setDefaultDialer(Instrumentation instrumentation, String packageName)570     private static String setDefaultDialer(Instrumentation instrumentation, String packageName)
571             throws Exception {
572         return executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName);
573     }
574 
getDefaultDialer(Instrumentation instrumentation)575     private static String getDefaultDialer(Instrumentation instrumentation) throws Exception {
576         return executeShellCommand(instrumentation, COMMAND_GET_DEFAULT_DIALER);
577     }
578 
579     /**
580      * Executes the given shell command and returns the output in a string. Note that even if we
581      * don't care about the output, we have to read the stream completely to make the command
582      * execute.
583      */
executeShellCommand(Instrumentation instrumentation, String command)584     private static String executeShellCommand(Instrumentation instrumentation,
585             String command) throws Exception {
586         final ParcelFileDescriptor parcelFileDescriptor =
587                 instrumentation.getUiAutomation().executeShellCommand(command);
588         BufferedReader bufferedReader = null;
589         try (InputStream in = new FileInputStream(parcelFileDescriptor.getFileDescriptor())) {
590             bufferedReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
591             String string = null;
592             StringBuilder out = new StringBuilder();
593             while ((string = bufferedReader.readLine()) != null) {
594                 out.append(string);
595             }
596             return out.toString();
597         } finally {
598             if (bufferedReader != null) {
599                 closeQuietly(bufferedReader);
600             }
601             closeQuietly(parcelFileDescriptor);
602         }
603     }
604 
closeQuietly(AutoCloseable closeable)605     private static void closeQuietly(AutoCloseable closeable) {
606         if (closeable != null) {
607             try {
608                 closeable.close();
609             } catch (RuntimeException rethrown) {
610                 throw rethrown;
611             } catch (Exception ignored) {
612                 // Quietly.
613             }
614         }
615     }
616 }
617