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