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