1 /* 2 * Copyright (C) 2008 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.content.cts; 18 19 import static android.content.ContentResolver.NOTIFY_INSERT; 20 import static android.content.ContentResolver.NOTIFY_UPDATE; 21 22 import android.accounts.Account; 23 import android.annotation.NonNull; 24 import android.annotation.UserIdInt; 25 import android.content.ContentProviderClient; 26 import android.content.ContentResolver; 27 import android.content.ContentResolver.MimeTypeInfo; 28 import android.content.ContentValues; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.PackageManager; 32 import android.content.res.AssetFileDescriptor; 33 import android.database.ContentObserver; 34 import android.database.Cursor; 35 import android.icu.text.Collator; 36 import android.icu.util.ULocale; 37 import android.net.Uri; 38 import android.os.Bundle; 39 import android.os.CancellationSignal; 40 import android.os.OperationCanceledException; 41 import android.os.ParcelFileDescriptor; 42 import android.os.RemoteException; 43 import android.os.UserHandle; 44 import android.platform.test.annotations.AppModeFull; 45 import android.test.AndroidTestCase; 46 import android.util.Log; 47 48 import androidx.test.InstrumentationRegistry; 49 50 import com.android.compatibility.common.util.PollingCheck; 51 import com.android.compatibility.common.util.ShellIdentityUtils; 52 import com.android.internal.annotations.GuardedBy; 53 import com.android.internal.util.ArrayUtils; 54 55 import java.io.File; 56 import java.io.FileInputStream; 57 import java.io.FileNotFoundException; 58 import java.io.IOException; 59 import java.io.InputStream; 60 import java.io.InputStreamReader; 61 import java.io.OutputStream; 62 import java.util.ArrayList; 63 import java.util.Arrays; 64 import java.util.Collection; 65 import java.util.HashSet; 66 import java.util.List; 67 import java.util.Objects; 68 import java.util.Set; 69 import java.util.concurrent.CountDownLatch; 70 import java.util.concurrent.TimeUnit; 71 72 public class ContentResolverTest extends AndroidTestCase { 73 private static final String TAG = "ContentResolverTest"; 74 75 private final static String COLUMN_ID_NAME = "_id"; 76 private final static String COLUMN_KEY_NAME = "key"; 77 private final static String COLUMN_VALUE_NAME = "value"; 78 79 private static final String AUTHORITY = "ctstest"; 80 private static final Uri TABLE1_URI = Uri.parse("content://" + AUTHORITY + "/testtable1/"); 81 private static final Uri TABLE1_CROSS_URI = 82 Uri.parse("content://" + AUTHORITY + "/testtable1/cross"); 83 private static final Uri TABLE2_URI = Uri.parse("content://" + AUTHORITY + "/testtable2/"); 84 85 private static final Uri LEVEL1_URI = Uri.parse("content://" + AUTHORITY + "/level/"); 86 private static final Uri LEVEL2_URI = Uri.parse("content://" + AUTHORITY + "/level/child"); 87 private static final Uri LEVEL3_URI = Uri.parse("content://" + AUTHORITY 88 + "/level/child/grandchild/"); 89 90 private static final String REMOTE_AUTHORITY = "remotectstest"; 91 private static final Uri REMOTE_TABLE1_URI = Uri.parse("content://" 92 + REMOTE_AUTHORITY + "/testtable1/"); 93 private static final Uri REMOTE_CRASH_URI = Uri.parse("content://" 94 + REMOTE_AUTHORITY + "/crash/"); 95 private static final Uri REMOTE_HANG_URI = Uri.parse("content://" 96 + REMOTE_AUTHORITY + "/hang/"); 97 98 private static final String RESTRICTED_AUTHORITY = "restrictedctstest"; 99 private static final Uri RESTRICTED_TABLE1_URI = 100 Uri.parse("content://" + RESTRICTED_AUTHORITY + "/testtable1/"); 101 private static final Uri RESTRICTED_TABLE1_ITEM_URI = 102 Uri.parse("content://" + RESTRICTED_AUTHORITY + "/testtable1/1"); 103 104 private static final Account ACCOUNT = new Account("cts", "cts"); 105 106 private static final String KEY1 = "key1"; 107 private static final String KEY2 = "key2"; 108 private static final String KEY3 = "key3"; 109 private static final int VALUE1 = 1; 110 private static final int VALUE2 = 2; 111 private static final int VALUE3 = 3; 112 113 private static final String TEST_PACKAGE_NAME = "android.content.cts"; 114 115 private Context mContext; 116 private ContentResolver mContentResolver; 117 private Cursor mCursor; 118 119 @Override setUp()120 protected void setUp() throws Exception { 121 super.setUp(); 122 123 mContext = getContext(); 124 mContentResolver = mContext.getContentResolver(); 125 126 MockContentProvider.setCrashOnLaunch(mContext, false); 127 128 // add three rows to database when every test case start. 129 ContentValues values = new ContentValues(); 130 131 values.put(COLUMN_KEY_NAME, KEY1); 132 values.put(COLUMN_VALUE_NAME, VALUE1); 133 mContentResolver.insert(TABLE1_URI, values); 134 mContentResolver.insert(REMOTE_TABLE1_URI, values); 135 136 values.put(COLUMN_KEY_NAME, KEY2); 137 values.put(COLUMN_VALUE_NAME, VALUE2); 138 mContentResolver.insert(TABLE1_URI, values); 139 mContentResolver.insert(REMOTE_TABLE1_URI, values); 140 141 values.put(COLUMN_KEY_NAME, KEY3); 142 values.put(COLUMN_VALUE_NAME, VALUE3); 143 mContentResolver.insert(TABLE1_URI, values); 144 mContentResolver.insert(REMOTE_TABLE1_URI, values); 145 } 146 147 @Override tearDown()148 protected void tearDown() throws Exception { 149 InstrumentationRegistry.getInstrumentation().getUiAutomation() 150 .dropShellPermissionIdentity(); 151 152 mContentResolver.delete(TABLE1_URI, null, null); 153 if ( null != mCursor && !mCursor.isClosed() ) { 154 mCursor.close(); 155 } 156 mContentResolver.delete(REMOTE_TABLE1_URI, null, null); 157 if ( null != mCursor && !mCursor.isClosed() ) { 158 mCursor.close(); 159 } 160 super.tearDown(); 161 } 162 testConstructor()163 public void testConstructor() { 164 assertNotNull(mContentResolver); 165 } 166 testCrashOnLaunch()167 public void testCrashOnLaunch() { 168 // This test is going to make sure that the platform deals correctly 169 // with a content provider process going away while a client is waiting 170 // for it to come up. 171 // First, we need to make sure our provider process is gone. Goodbye! 172 ContentProviderClient client = mContentResolver.acquireContentProviderClient( 173 REMOTE_AUTHORITY); 174 // We are going to do something wrong here... release the client first, 175 // so the act of killing it doesn't kill our own process. 176 client.release(); 177 try { 178 client.delete(REMOTE_CRASH_URI, null, null); 179 } catch (RemoteException e) { 180 } 181 // Now make sure the thing is actually gone. 182 boolean gone = true; 183 try { 184 client.getType(REMOTE_TABLE1_URI); 185 gone = false; 186 } catch (RemoteException e) { 187 } 188 if (!gone) { 189 fail("Content provider process is not gone!"); 190 } 191 try { 192 MockContentProvider.setCrashOnLaunch(mContext, true); 193 String type1 = mContentResolver.getType(REMOTE_TABLE1_URI); 194 assertFalse(MockContentProvider.getCrashOnLaunch(mContext)); 195 assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 196 } finally { 197 MockContentProvider.setCrashOnLaunch(mContext, false); 198 } 199 } 200 testUnstableToStableRefs()201 public void testUnstableToStableRefs() { 202 // Get an unstable refrence on the remote content provider. 203 ContentProviderClient uClient = mContentResolver.acquireUnstableContentProviderClient( 204 REMOTE_AUTHORITY); 205 // Verify we can access it. 206 String type1 = mContentResolver.getType(REMOTE_TABLE1_URI); 207 assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 208 209 // Get a stable reference on the remote content provider. 210 ContentProviderClient sClient = mContentResolver.acquireContentProviderClient( 211 REMOTE_AUTHORITY); 212 // Verify we can still access it. 213 type1 = mContentResolver.getType(REMOTE_TABLE1_URI); 214 assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 215 216 // Release unstable reference. 217 uClient.release(); 218 // Verify we can still access it. 219 type1 = mContentResolver.getType(REMOTE_TABLE1_URI); 220 assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 221 222 // Release stable reference, removing last ref. 223 sClient.release(); 224 // Kill it. Note that a bug at this point where it causes our own 225 // process to be killed will result in the entire test failing. 226 try { 227 Log.i("ContentResolverTest", 228 "Killing remote client -- if test process goes away, that is why!"); 229 uClient.delete(REMOTE_CRASH_URI, null, null); 230 } catch (RemoteException e) { 231 } 232 // Make sure the remote client is actually gone. 233 boolean gone = true; 234 try { 235 sClient.getType(REMOTE_TABLE1_URI); 236 gone = false; 237 } catch (RemoteException e) { 238 } 239 if (!gone) { 240 fail("Content provider process is not gone!"); 241 } 242 } 243 testStableToUnstableRefs()244 public void testStableToUnstableRefs() { 245 // Get a stable reference on the remote content provider. 246 ContentProviderClient sClient = mContentResolver.acquireContentProviderClient( 247 REMOTE_AUTHORITY); 248 // Verify we can still access it. 249 String type1 = mContentResolver.getType(REMOTE_TABLE1_URI); 250 assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 251 252 // Get an unstable refrence on the remote content provider. 253 ContentProviderClient uClient = mContentResolver.acquireUnstableContentProviderClient( 254 REMOTE_AUTHORITY); 255 // Verify we can access it. 256 type1 = mContentResolver.getType(REMOTE_TABLE1_URI); 257 assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 258 259 // Release stable reference, leaving only an unstable ref. 260 sClient.release(); 261 262 // Kill it. Note that a bug at this point where it causes our own 263 // process to be killed will result in the entire test failing. 264 try { 265 Log.i("ContentResolverTest", 266 "Killing remote client -- if test process goes away, that is why!"); 267 uClient.delete(REMOTE_CRASH_URI, null, null); 268 } catch (RemoteException e) { 269 } 270 // Make sure the remote client is actually gone. 271 boolean gone = true; 272 try { 273 uClient.getType(REMOTE_TABLE1_URI); 274 gone = false; 275 } catch (RemoteException e) { 276 } 277 if (!gone) { 278 fail("Content provider process is not gone!"); 279 } 280 281 // Release unstable reference. 282 uClient.release(); 283 } 284 testGetType()285 public void testGetType() { 286 String type1 = mContentResolver.getType(TABLE1_URI); 287 assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 288 289 String type2 = mContentResolver.getType(TABLE2_URI); 290 assertTrue(type2.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 291 292 Uri invalidUri = Uri.parse("abc"); 293 assertNull(mContentResolver.getType(invalidUri)); 294 295 try { 296 mContentResolver.getType(null); 297 fail("did not throw NullPointerException when Uri is null."); 298 } catch (NullPointerException e) { 299 //expected. 300 } 301 } 302 303 @AppModeFull testGetTypeAnonymous()304 public void testGetTypeAnonymous() { 305 String type1 = mContentResolver.getType(RESTRICTED_TABLE1_URI); 306 assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 307 String type2 = mContentResolver.getType(RESTRICTED_TABLE1_ITEM_URI); 308 assertNull(type2); 309 } 310 testUnstableGetType()311 public void testUnstableGetType() { 312 // Get an unstable refrence on the remote content provider. 313 ContentProviderClient client = mContentResolver.acquireUnstableContentProviderClient( 314 REMOTE_AUTHORITY); 315 // Verify we can access it. 316 String type1 = mContentResolver.getType(REMOTE_TABLE1_URI); 317 assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 318 319 // Kill it. Note that a bug at this point where it causes our own 320 // process to be killed will result in the entire test failing. 321 try { 322 Log.i("ContentResolverTest", 323 "Killing remote client -- if test process goes away, that is why!"); 324 client.delete(REMOTE_CRASH_URI, null, null); 325 } catch (RemoteException e) { 326 } 327 // Make sure the remote client is actually gone. 328 boolean gone = true; 329 try { 330 client.getType(REMOTE_TABLE1_URI); 331 gone = false; 332 } catch (RemoteException e) { 333 } 334 if (!gone) { 335 fail("Content provider process is not gone!"); 336 } 337 338 // Now the remote client is gone, can we recover? 339 // Release our old reference. 340 client.release(); 341 // Get a new reference. 342 client = mContentResolver.acquireUnstableContentProviderClient(REMOTE_AUTHORITY); 343 // Verify we can access it. 344 type1 = mContentResolver.getType(REMOTE_TABLE1_URI); 345 assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0)); 346 } 347 testQuery()348 public void testQuery() { 349 mCursor = mContentResolver.query(TABLE1_URI, null, null, null); 350 351 assertNotNull(mCursor); 352 assertEquals(3, mCursor.getCount()); 353 assertEquals(3, mCursor.getColumnCount()); 354 355 mCursor.moveToLast(); 356 assertEquals(3, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 357 assertEquals(KEY3, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 358 assertEquals(VALUE3, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 359 360 mCursor.moveToPrevious(); 361 assertEquals(2, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 362 assertEquals(KEY2, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 363 assertEquals(VALUE2, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 364 mCursor.close(); 365 } 366 testQuery_WithSqlSelectionArgs()367 public void testQuery_WithSqlSelectionArgs() { 368 Bundle queryArgs = new Bundle(); 369 queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, COLUMN_ID_NAME + "=?"); 370 queryArgs.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, new String[] {"1"}); 371 372 mCursor = mContentResolver.query(TABLE1_URI, null, queryArgs, null); 373 assertNotNull(mCursor); 374 assertEquals(1, mCursor.getCount()); 375 assertEquals(3, mCursor.getColumnCount()); 376 377 mCursor.moveToFirst(); 378 assertEquals(1, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 379 assertEquals(KEY1, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 380 assertEquals(VALUE1, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 381 mCursor.close(); 382 383 queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, COLUMN_KEY_NAME + "=?"); 384 queryArgs.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, new String[] {KEY3}); 385 mCursor = mContentResolver.query(TABLE1_URI, null, queryArgs, null); 386 assertNotNull(mCursor); 387 assertEquals(1, mCursor.getCount()); 388 assertEquals(3, mCursor.getColumnCount()); 389 390 mCursor.moveToFirst(); 391 assertEquals(3, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 392 assertEquals(KEY3, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 393 assertEquals(VALUE3, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 394 mCursor.close(); 395 } 396 397 /* 398 * NOTE: this test is implicitly coupled to the implementation 399 * of MockContentProvider#query, specifically the facts: 400 * 401 * - it does *not* override the query w/ Bundle methods 402 * - it receives the auto-generated sql format arguments (supplied by the framework) 403 * - it is backed by sqlite and forwards the sql formatted args. 404 */ testQuery_SqlSortingFromBundleArgs()405 public void testQuery_SqlSortingFromBundleArgs() { 406 407 mContentResolver.delete(TABLE1_URI, null, null); 408 ContentValues values = new ContentValues(); 409 410 values.put(COLUMN_KEY_NAME, "0"); 411 values.put(COLUMN_VALUE_NAME, "abc"); 412 mContentResolver.insert(TABLE1_URI, values); 413 414 values.put(COLUMN_KEY_NAME, "1"); 415 values.put(COLUMN_VALUE_NAME, "DEF"); 416 mContentResolver.insert(TABLE1_URI, values); 417 418 values.put(COLUMN_KEY_NAME, "2"); 419 values.put(COLUMN_VALUE_NAME, "ghi"); 420 mContentResolver.insert(TABLE1_URI, values); 421 422 String[] sortCols = new String[] { COLUMN_VALUE_NAME }; 423 Bundle queryArgs = new Bundle(); 424 queryArgs.putStringArray( 425 ContentResolver.QUERY_ARG_SORT_COLUMNS, 426 sortCols); 427 428 // Sort ascending... 429 queryArgs.putInt( 430 ContentResolver.QUERY_ARG_SORT_DIRECTION, 431 ContentResolver.QUERY_SORT_DIRECTION_ASCENDING); 432 433 mCursor = mContentResolver.query(TABLE1_URI, sortCols, queryArgs, null); 434 int col = mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME); 435 436 mCursor.moveToNext(); 437 assertEquals("DEF", mCursor.getString(col)); 438 mCursor.moveToNext(); 439 assertEquals("abc", mCursor.getString(col)); 440 mCursor.moveToNext(); 441 assertEquals("ghi", mCursor.getString(col)); 442 443 mCursor.close(); 444 445 // Nocase collation, descending... 446 queryArgs.putInt( 447 ContentResolver.QUERY_ARG_SORT_DIRECTION, 448 ContentResolver.QUERY_SORT_DIRECTION_DESCENDING); 449 queryArgs.putInt( 450 ContentResolver.QUERY_ARG_SORT_COLLATION, 451 java.text.Collator.SECONDARY); 452 453 mCursor = mContentResolver.query(TABLE1_URI, null, queryArgs, null); 454 col = mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME); 455 456 mCursor.moveToNext(); 457 assertEquals("ghi", mCursor.getString(col)); 458 mCursor.moveToNext(); 459 assertEquals("DEF", mCursor.getString(col)); 460 mCursor.moveToNext(); 461 assertEquals("abc", mCursor.getString(col)); 462 463 mCursor.close(); 464 } 465 testQuery_SqlSortingFromBundleArgs_Locale()466 public void testQuery_SqlSortingFromBundleArgs_Locale() { 467 mContentResolver.delete(TABLE1_URI, null, null); 468 469 final List<String> data = Arrays.asList( 470 "ABC", "abc", "pinyin", "가나다", "바사", "테스트", "马", 471 "嘛", "妈", "骂", "吗", "码", "玛", "麻", "中", "梵", "苹果", "久了", "伺候"); 472 473 for (String s : data) { 474 final ContentValues values = new ContentValues(); 475 values.put(COLUMN_KEY_NAME, s.hashCode()); 476 values.put(COLUMN_VALUE_NAME, s); 477 mContentResolver.insert(TABLE1_URI, values); 478 } 479 480 String[] sortCols = new String[] { COLUMN_VALUE_NAME }; 481 Bundle queryArgs = new Bundle(); 482 queryArgs.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, sortCols); 483 484 for (String locale : new String[] { 485 "zh", 486 "zh@collation=pinyin", 487 "zh@collation=stroke", 488 "zh@collation=zhuyin", 489 }) { 490 // Assert that sorting is identical between SQLite and ICU4J 491 queryArgs.putString(ContentResolver.QUERY_ARG_SORT_LOCALE, locale); 492 try (Cursor c = mContentResolver.query(TABLE1_URI, sortCols, queryArgs, null)) { 493 data.sort(Collator.getInstance(new ULocale(locale))); 494 assertEquals(data, collect(c)); 495 } 496 } 497 } 498 collect(Cursor c)499 private static List<String> collect(Cursor c) { 500 List<String> res = new ArrayList<>(); 501 while (c.moveToNext()) { 502 res.add(c.getString(0)); 503 } 504 return res; 505 } 506 507 /** 508 * Verifies that paging information is correctly relayed, and that 509 * honored arguments from a supporting client are returned correctly. 510 */ testQuery_PagedResults()511 public void testQuery_PagedResults() { 512 513 Bundle queryArgs = new Bundle(); 514 queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 10); 515 queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 3); 516 queryArgs.putInt(TestPagingContentProvider.RECORD_COUNT, 100); 517 518 mCursor = mContentResolver.query( 519 TestPagingContentProvider.PAGED_DATA_URI, null, queryArgs, null); 520 521 Bundle extras = mCursor.getExtras(); 522 extras = extras != null ? extras : Bundle.EMPTY; 523 524 assertEquals(3, mCursor.getCount()); 525 assertTrue(extras.containsKey(ContentResolver.EXTRA_TOTAL_COUNT)); 526 assertEquals(100, extras.getInt(ContentResolver.EXTRA_TOTAL_COUNT)); 527 528 String[] honoredArgs = extras.getStringArray(ContentResolver.EXTRA_HONORED_ARGS); 529 assertNotNull(honoredArgs); 530 assertTrue(ArrayUtils.contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET)); 531 assertTrue(ArrayUtils.contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT)); 532 533 int col = mCursor.getColumnIndexOrThrow(TestPagingContentProvider.COLUMN_POS); 534 535 mCursor.moveToNext(); 536 assertEquals(10, mCursor.getInt(col)); 537 mCursor.moveToNext(); 538 assertEquals(11, mCursor.getInt(col)); 539 mCursor.moveToNext(); 540 assertEquals(12, mCursor.getInt(col)); 541 542 assertFalse(mCursor.moveToNext()); 543 544 mCursor.close(); 545 } 546 testQuery_NullUriThrows()547 public void testQuery_NullUriThrows() { 548 try { 549 mContentResolver.query(null, null, null, null, null); 550 fail("did not throw NullPointerException when uri is null."); 551 } catch (NullPointerException e) { 552 //expected. 553 } 554 } 555 testCrashingQuery()556 public void testCrashingQuery() { 557 try { 558 MockContentProvider.setCrashOnLaunch(mContext, true); 559 mCursor = mContentResolver.query(REMOTE_CRASH_URI, null, null, null, null); 560 assertFalse(MockContentProvider.getCrashOnLaunch(mContext)); 561 } finally { 562 MockContentProvider.setCrashOnLaunch(mContext, false); 563 } 564 565 assertNotNull(mCursor); 566 assertEquals(3, mCursor.getCount()); 567 assertEquals(3, mCursor.getColumnCount()); 568 569 mCursor.moveToLast(); 570 assertEquals(3, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 571 assertEquals(KEY3, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 572 assertEquals(VALUE3, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 573 574 mCursor.moveToPrevious(); 575 assertEquals(2, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 576 assertEquals(KEY2, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 577 assertEquals(VALUE2, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 578 mCursor.close(); 579 } 580 testCancelableQuery_WhenNotCanceled_ReturnsResultSet()581 public void testCancelableQuery_WhenNotCanceled_ReturnsResultSet() { 582 CancellationSignal cancellationSignal = new CancellationSignal(); 583 584 Cursor cursor = mContentResolver.query(TABLE1_URI, null, null, null, null, 585 cancellationSignal); 586 assertEquals(3, cursor.getCount()); 587 cursor.close(); 588 } 589 testCancelableQuery_WhenCanceledBeforeQuery_ThrowsImmediately()590 public void testCancelableQuery_WhenCanceledBeforeQuery_ThrowsImmediately() { 591 CancellationSignal cancellationSignal = new CancellationSignal(); 592 cancellationSignal.cancel(); 593 594 try { 595 mContentResolver.query(TABLE1_URI, null, null, null, null, cancellationSignal); 596 fail("Expected OperationCanceledException"); 597 } catch (OperationCanceledException ex) { 598 // expected 599 } 600 } 601 testCancelableQuery_WhenCanceledDuringLongRunningQuery_CancelsQueryAndThrows()602 public void testCancelableQuery_WhenCanceledDuringLongRunningQuery_CancelsQueryAndThrows() { 603 // Populate a table with a bunch of integers. 604 mContentResolver.delete(TABLE1_URI, null, null); 605 ContentValues values = new ContentValues(); 606 for (int i = 0; i < 100; i++) { 607 values.put(COLUMN_KEY_NAME, i); 608 values.put(COLUMN_VALUE_NAME, i); 609 mContentResolver.insert(TABLE1_URI, values); 610 } 611 612 for (int i = 0; i < 5; i++) { 613 final CancellationSignal cancellationSignal = new CancellationSignal(); 614 Thread cancellationThread = new Thread() { 615 @Override 616 public void run() { 617 try { 618 Thread.sleep(300); 619 } catch (InterruptedException ex) { 620 } 621 cancellationSignal.cancel(); 622 } 623 }; 624 try { 625 // Build an unsatisfiable 5-way cross-product query over 100 values but 626 // produces no output. This should force SQLite to loop for a long time 627 // as it tests 10^10 combinations. 628 cancellationThread.start(); 629 630 final long startTime = System.nanoTime(); 631 try { 632 mContentResolver.query(TABLE1_CROSS_URI, null, 633 "a.value + b.value + c.value + d.value + e.value > 1000000", 634 null, null, cancellationSignal); 635 fail("Expected OperationCanceledException"); 636 } catch (OperationCanceledException ex) { 637 // expected 638 } 639 640 // We want to confirm that the query really was running and then got 641 // canceled midway. 642 final long waitTime = System.nanoTime() - startTime; 643 if (waitTime > 150 * 1000000L && waitTime < 600 * 1000000L) { 644 return; // success! 645 } 646 } finally { 647 try { 648 cancellationThread.join(); 649 } catch (InterruptedException e) { 650 } 651 } 652 } 653 654 // Occasionally we might miss the timing deadline due to factors in the 655 // environment, but if after several trials we still couldn't demonstrate 656 // that the query was canceled, then the test must be broken. 657 fail("Could not prove that the query actually canceled midway during execution."); 658 } 659 testOpenInputStream()660 public void testOpenInputStream() throws IOException { 661 final Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + 662 "://" + TEST_PACKAGE_NAME + "/" + R.drawable.pass); 663 664 InputStream is = mContentResolver.openInputStream(uri); 665 assertNotNull(is); 666 is.close(); 667 668 final Uri invalidUri = Uri.parse("abc"); 669 try { 670 mContentResolver.openInputStream(invalidUri); 671 fail("did not throw FileNotFoundException when uri is invalid."); 672 } catch (FileNotFoundException e) { 673 //expected. 674 } 675 } 676 testOpenOutputStream()677 public void testOpenOutputStream() throws IOException { 678 Uri uri = Uri.parse(ContentResolver.SCHEME_FILE + "://" + 679 getContext().getCacheDir().getAbsolutePath() + 680 "/temp.jpg"); 681 OutputStream os = mContentResolver.openOutputStream(uri); 682 assertNotNull(os); 683 os.close(); 684 685 os = mContentResolver.openOutputStream(uri, "wa"); 686 assertNotNull(os); 687 os.close(); 688 689 uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + 690 "://" + TEST_PACKAGE_NAME + "/" + R.raw.testimage); 691 try { 692 mContentResolver.openOutputStream(uri); 693 fail("did not throw FileNotFoundException when scheme is not accepted."); 694 } catch (FileNotFoundException e) { 695 //expected. 696 } 697 698 try { 699 mContentResolver.openOutputStream(uri, "w"); 700 fail("did not throw FileNotFoundException when scheme is not accepted."); 701 } catch (FileNotFoundException e) { 702 //expected. 703 } 704 705 Uri invalidUri = Uri.parse("abc"); 706 try { 707 mContentResolver.openOutputStream(invalidUri); 708 fail("did not throw FileNotFoundException when uri is invalid."); 709 } catch (FileNotFoundException e) { 710 //expected. 711 } 712 713 try { 714 mContentResolver.openOutputStream(invalidUri, "w"); 715 fail("did not throw FileNotFoundException when uri is invalid."); 716 } catch (FileNotFoundException e) { 717 //expected. 718 } 719 } 720 testOpenAssetFileDescriptor()721 public void testOpenAssetFileDescriptor() throws IOException { 722 Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + 723 "://" + TEST_PACKAGE_NAME + "/" + R.raw.testimage); 724 725 AssetFileDescriptor afd = mContentResolver.openAssetFileDescriptor(uri, "r"); 726 assertNotNull(afd); 727 afd.close(); 728 729 try { 730 mContentResolver.openAssetFileDescriptor(uri, "d"); 731 fail("did not throw FileNotFoundException when mode is unknown."); 732 } catch (FileNotFoundException e) { 733 //expected. 734 } 735 736 Uri invalidUri = Uri.parse("abc"); 737 try { 738 mContentResolver.openAssetFileDescriptor(invalidUri, "r"); 739 fail("did not throw FileNotFoundException when uri is invalid."); 740 } catch (FileNotFoundException e) { 741 //expected. 742 } 743 } 744 consumeAssetFileDescriptor(AssetFileDescriptor afd)745 private String consumeAssetFileDescriptor(AssetFileDescriptor afd) 746 throws IOException { 747 FileInputStream stream = null; 748 try { 749 stream = afd.createInputStream(); 750 InputStreamReader reader = new InputStreamReader(stream, "UTF-8"); 751 752 // Got it... copy the stream into a local string and return it. 753 StringBuilder builder = new StringBuilder(128); 754 char[] buffer = new char[8192]; 755 int len; 756 while ((len=reader.read(buffer)) > 0) { 757 builder.append(buffer, 0, len); 758 } 759 return builder.toString(); 760 761 } finally { 762 if (stream != null) { 763 try { 764 stream.close(); 765 } catch (IOException e) { 766 } 767 } 768 } 769 770 } 771 testCrashingOpenAssetFileDescriptor()772 public void testCrashingOpenAssetFileDescriptor() throws IOException { 773 AssetFileDescriptor afd = null; 774 try { 775 MockContentProvider.setCrashOnLaunch(mContext, true); 776 afd = mContentResolver.openAssetFileDescriptor(REMOTE_CRASH_URI, "rw"); 777 assertFalse(MockContentProvider.getCrashOnLaunch(mContext)); 778 assertNotNull(afd); 779 String str = consumeAssetFileDescriptor(afd); 780 afd = null; 781 assertEquals(str, "This is the openAssetFile test data!"); 782 } finally { 783 MockContentProvider.setCrashOnLaunch(mContext, false); 784 if (afd != null) { 785 afd.close(); 786 } 787 } 788 789 // Make sure a content provider crash at this point won't hurt us. 790 ContentProviderClient uClient = mContentResolver.acquireUnstableContentProviderClient( 791 REMOTE_AUTHORITY); 792 // Kill it. Note that a bug at this point where it causes our own 793 // process to be killed will result in the entire test failing. 794 try { 795 Log.i("ContentResolverTest", 796 "Killing remote client -- if test process goes away, that is why!"); 797 uClient.delete(REMOTE_CRASH_URI, null, null); 798 } catch (RemoteException e) { 799 } 800 uClient.release(); 801 } 802 testCrashingOpenTypedAssetFileDescriptor()803 public void testCrashingOpenTypedAssetFileDescriptor() throws IOException { 804 AssetFileDescriptor afd = null; 805 try { 806 MockContentProvider.setCrashOnLaunch(mContext, true); 807 afd = mContentResolver.openTypedAssetFileDescriptor( 808 REMOTE_CRASH_URI, "text/plain", null); 809 assertFalse(MockContentProvider.getCrashOnLaunch(mContext)); 810 assertNotNull(afd); 811 String str = consumeAssetFileDescriptor(afd); 812 afd = null; 813 assertEquals(str, "This is the openTypedAssetFile test data!"); 814 } finally { 815 MockContentProvider.setCrashOnLaunch(mContext, false); 816 if (afd != null) { 817 afd.close(); 818 } 819 } 820 821 // Make sure a content provider crash at this point won't hurt us. 822 ContentProviderClient uClient = mContentResolver.acquireUnstableContentProviderClient( 823 REMOTE_AUTHORITY); 824 // Kill it. Note that a bug at this point where it causes our own 825 // process to be killed will result in the entire test failing. 826 try { 827 Log.i("ContentResolverTest", 828 "Killing remote client -- if test process goes away, that is why!"); 829 uClient.delete(REMOTE_CRASH_URI, null, null); 830 } catch (RemoteException e) { 831 } 832 uClient.release(); 833 } 834 testOpenFileDescriptor()835 public void testOpenFileDescriptor() throws IOException { 836 Uri uri = Uri.parse(ContentResolver.SCHEME_FILE + "://" + 837 getContext().getCacheDir().getAbsolutePath() + 838 "/temp.jpg"); 839 ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(uri, "w"); 840 assertNotNull(pfd); 841 pfd.close(); 842 843 try { 844 mContentResolver.openFileDescriptor(uri, "d"); 845 fail("did not throw IllegalArgumentException when mode is unknown."); 846 } catch (IllegalArgumentException e) { 847 //expected. 848 } 849 850 Uri invalidUri = Uri.parse("abc"); 851 try { 852 mContentResolver.openFileDescriptor(invalidUri, "w"); 853 fail("did not throw FileNotFoundException when uri is invalid."); 854 } catch (FileNotFoundException e) { 855 //expected. 856 } 857 858 uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + 859 "://" + TEST_PACKAGE_NAME + "/" + R.raw.testimage); 860 try { 861 mContentResolver.openFileDescriptor(uri, "w"); 862 fail("did not throw FileNotFoundException when scheme is not accepted."); 863 } catch (FileNotFoundException e) { 864 //expected. 865 } 866 } 867 testInsert()868 public void testInsert() { 869 String key4 = "key4"; 870 String key5 = "key5"; 871 int value4 = 4; 872 int value5 = 5; 873 String key4Selection = COLUMN_KEY_NAME + "=\"" + key4 + "\""; 874 875 mCursor = mContentResolver.query(TABLE1_URI, null, key4Selection, null, null); 876 assertEquals(0, mCursor.getCount()); 877 mCursor.close(); 878 879 ContentValues values = new ContentValues(); 880 values.put(COLUMN_KEY_NAME, key4); 881 values.put(COLUMN_VALUE_NAME, value4); 882 Uri uri = mContentResolver.insert(TABLE1_URI, values); 883 assertNotNull(uri); 884 885 mCursor = mContentResolver.query(TABLE1_URI, null, key4Selection, null, null); 886 assertNotNull(mCursor); 887 assertEquals(1, mCursor.getCount()); 888 889 mCursor.moveToFirst(); 890 assertEquals(4, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 891 assertEquals(key4, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 892 assertEquals(value4, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 893 mCursor.close(); 894 895 values.put(COLUMN_KEY_NAME, key5); 896 values.put(COLUMN_VALUE_NAME, value5); 897 uri = mContentResolver.insert(TABLE1_URI, values); 898 assertNotNull(uri); 899 900 // check returned uri 901 mCursor = mContentResolver.query(uri, null, null, null, null); 902 assertNotNull(mCursor); 903 assertEquals(1, mCursor.getCount()); 904 905 mCursor.moveToLast(); 906 assertEquals(5, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 907 assertEquals(key5, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 908 assertEquals(value5, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 909 mCursor.close(); 910 911 try { 912 mContentResolver.insert(null, values); 913 fail("did not throw NullPointerException when uri is null."); 914 } catch (NullPointerException e) { 915 //expected. 916 } 917 } 918 testBulkInsert()919 public void testBulkInsert() { 920 String key4 = "key4"; 921 String key5 = "key5"; 922 int value4 = 4; 923 int value5 = 5; 924 925 mCursor = mContentResolver.query(TABLE1_URI, null, null, null, null); 926 assertNotNull(mCursor); 927 assertEquals(3, mCursor.getCount()); 928 mCursor.close(); 929 930 ContentValues[] cvs = new ContentValues[2]; 931 cvs[0] = new ContentValues(); 932 cvs[0].put(COLUMN_KEY_NAME, key4); 933 cvs[0].put(COLUMN_VALUE_NAME, value4); 934 935 cvs[1] = new ContentValues(); 936 cvs[1].put(COLUMN_KEY_NAME, key5); 937 cvs[1].put(COLUMN_VALUE_NAME, value5); 938 939 assertEquals(2, mContentResolver.bulkInsert(TABLE1_URI, cvs)); 940 mCursor = mContentResolver.query(TABLE1_URI, null, null, null, null); 941 assertNotNull(mCursor); 942 assertEquals(5, mCursor.getCount()); 943 944 mCursor.moveToLast(); 945 assertEquals(5, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 946 assertEquals(key5, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 947 assertEquals(value5, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 948 949 mCursor.moveToPrevious(); 950 assertEquals(4, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 951 assertEquals(key4, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 952 assertEquals(value4, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 953 mCursor.close(); 954 955 try { 956 mContentResolver.bulkInsert(null, cvs); 957 fail("did not throw NullPointerException when uri is null."); 958 } catch (NullPointerException e) { 959 //expected. 960 } 961 } 962 testDelete()963 public void testDelete() { 964 mCursor = mContentResolver.query(TABLE1_URI, null, null, null, null); 965 assertNotNull(mCursor); 966 assertEquals(3, mCursor.getCount()); 967 mCursor.close(); 968 969 assertEquals(3, mContentResolver.delete(TABLE1_URI, null, null)); 970 mCursor = mContentResolver.query(TABLE1_URI, null, null, null, null); 971 assertNotNull(mCursor); 972 assertEquals(0, mCursor.getCount()); 973 mCursor.close(); 974 975 // add three rows to database. 976 ContentValues values = new ContentValues(); 977 values.put(COLUMN_KEY_NAME, KEY1); 978 values.put(COLUMN_VALUE_NAME, VALUE1); 979 mContentResolver.insert(TABLE1_URI, values); 980 981 values.put(COLUMN_KEY_NAME, KEY2); 982 values.put(COLUMN_VALUE_NAME, VALUE2); 983 mContentResolver.insert(TABLE1_URI, values); 984 985 values.put(COLUMN_KEY_NAME, KEY3); 986 values.put(COLUMN_VALUE_NAME, VALUE3); 987 mContentResolver.insert(TABLE1_URI, values); 988 989 // test delete row using selection 990 String selection = COLUMN_ID_NAME + "=2"; 991 assertEquals(1, mContentResolver.delete(TABLE1_URI, selection, null)); 992 993 mCursor = mContentResolver.query(TABLE1_URI, null, null, null, null); 994 assertNotNull(mCursor); 995 assertEquals(2, mCursor.getCount()); 996 997 mCursor.moveToFirst(); 998 assertEquals(1, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 999 assertEquals(KEY1, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 1000 assertEquals(VALUE1, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 1001 1002 mCursor.moveToNext(); 1003 assertEquals(3, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 1004 assertEquals(KEY3, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 1005 assertEquals(VALUE3, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 1006 mCursor.close(); 1007 1008 selection = COLUMN_VALUE_NAME + "=3"; 1009 assertEquals(1, mContentResolver.delete(TABLE1_URI, selection, null)); 1010 1011 mCursor = mContentResolver.query(TABLE1_URI, null, null, null, null); 1012 assertNotNull(mCursor); 1013 assertEquals(1, mCursor.getCount()); 1014 1015 mCursor.moveToFirst(); 1016 assertEquals(1, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 1017 assertEquals(KEY1, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 1018 assertEquals(VALUE1, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 1019 mCursor.close(); 1020 1021 selection = COLUMN_KEY_NAME + "=\"" + KEY1 + "\""; 1022 assertEquals(1, mContentResolver.delete(TABLE1_URI, selection, null)); 1023 1024 mCursor = mContentResolver.query(TABLE1_URI, null, null, null, null); 1025 assertNotNull(mCursor); 1026 assertEquals(0, mCursor.getCount()); 1027 mCursor.close(); 1028 1029 try { 1030 mContentResolver.delete(null, null, null); 1031 fail("did not throw NullPointerException when uri is null."); 1032 } catch (NullPointerException e) { 1033 //expected. 1034 } 1035 } 1036 testUpdate()1037 public void testUpdate() { 1038 ContentValues values = new ContentValues(); 1039 String key10 = "key10"; 1040 String key20 = "key20"; 1041 int value10 = 10; 1042 int value20 = 20; 1043 1044 values.put(COLUMN_KEY_NAME, key10); 1045 values.put(COLUMN_VALUE_NAME, value10); 1046 1047 // test update all the rows. 1048 assertEquals(3, mContentResolver.update(TABLE1_URI, values, null, null)); 1049 mCursor = mContentResolver.query(TABLE1_URI, null, null, null, null); 1050 assertNotNull(mCursor); 1051 assertEquals(3, mCursor.getCount()); 1052 1053 mCursor.moveToFirst(); 1054 assertEquals(1, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 1055 assertEquals(key10, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 1056 assertEquals(value10, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 1057 1058 mCursor.moveToNext(); 1059 assertEquals(2, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 1060 assertEquals(key10, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 1061 assertEquals(value10, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 1062 1063 mCursor.moveToLast(); 1064 assertEquals(3, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 1065 assertEquals(key10, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 1066 assertEquals(value10, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 1067 mCursor.close(); 1068 1069 // test update one row using selection. 1070 String selection = COLUMN_ID_NAME + "=1"; 1071 values.put(COLUMN_KEY_NAME, key20); 1072 values.put(COLUMN_VALUE_NAME, value20); 1073 1074 assertEquals(1, mContentResolver.update(TABLE1_URI, values, selection, null)); 1075 mCursor = mContentResolver.query(TABLE1_URI, null, null, null, null); 1076 assertNotNull(mCursor); 1077 assertEquals(3, mCursor.getCount()); 1078 1079 mCursor.moveToFirst(); 1080 assertEquals(1, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 1081 assertEquals(key20, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 1082 assertEquals(value20, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 1083 1084 mCursor.moveToNext(); 1085 assertEquals(2, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 1086 assertEquals(key10, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 1087 assertEquals(value10, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 1088 1089 mCursor.moveToLast(); 1090 assertEquals(3, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_ID_NAME))); 1091 assertEquals(key10, mCursor.getString(mCursor.getColumnIndexOrThrow(COLUMN_KEY_NAME))); 1092 assertEquals(value10, mCursor.getInt(mCursor.getColumnIndexOrThrow(COLUMN_VALUE_NAME))); 1093 mCursor.close(); 1094 1095 try { 1096 mContentResolver.update(null, values, null, null); 1097 fail("did not throw NullPointerException when uri is null."); 1098 } catch (NullPointerException e) { 1099 //expected. 1100 } 1101 1102 // javadoc says it will throw NullPointerException when values are null, 1103 // but actually, it throws IllegalArgumentException here. 1104 try { 1105 mContentResolver.update(TABLE1_URI, null, null, null); 1106 fail("did not throw IllegalArgumentException when values are null."); 1107 } catch (IllegalArgumentException e) { 1108 //expected. 1109 } 1110 } 1111 testRefresh_DefaultImplReturnsFalse()1112 public void testRefresh_DefaultImplReturnsFalse() { 1113 boolean refreshed = mContentResolver.refresh(TABLE1_URI, null, null); 1114 assertFalse(refreshed); 1115 MockContentProvider.assertRefreshed(TABLE1_URI); 1116 } 1117 testRefresh_ReturnsProviderValue()1118 public void testRefresh_ReturnsProviderValue() { 1119 try { 1120 MockContentProvider.setRefreshReturnValue(true); 1121 boolean refreshed = mContentResolver.refresh(TABLE1_URI, null, null); 1122 assertTrue(refreshed); 1123 MockContentProvider.assertRefreshed(TABLE1_URI); 1124 } finally { 1125 MockContentProvider.setRefreshReturnValue(false); 1126 } 1127 } 1128 testRefresh_NullUriThrowsImmediately()1129 public void testRefresh_NullUriThrowsImmediately() { 1130 try { 1131 mContentResolver.refresh(null, null, null); 1132 fail("did not throw NullPointerException when uri is null."); 1133 } catch (NullPointerException e) { 1134 //expected. 1135 } 1136 } 1137 testRefresh_CancellableThrowsImmediately()1138 public void testRefresh_CancellableThrowsImmediately() { 1139 CancellationSignal cancellationSignal = new CancellationSignal(); 1140 cancellationSignal.cancel(); 1141 1142 try { 1143 mContentResolver.refresh(TABLE1_URI, null, cancellationSignal); 1144 fail("Expected OperationCanceledException"); 1145 } catch (OperationCanceledException ex) { 1146 // expected 1147 } 1148 } 1149 testCheckUriPermission()1150 public void testCheckUriPermission() { 1151 assertEquals(PackageManager.PERMISSION_GRANTED, mContentResolver.checkUriPermission( 1152 TABLE1_URI, android.os.Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION)); 1153 assertEquals(PackageManager.PERMISSION_DENIED, mContentResolver.checkUriPermission( 1154 TABLE1_URI, android.os.Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION)); 1155 } 1156 testRegisterContentObserver()1157 public void testRegisterContentObserver() { 1158 final MockContentObserver mco = new MockContentObserver(); 1159 1160 mContentResolver.registerContentObserver(TABLE1_URI, true, mco); 1161 assertFalse(mco.hadOnChanged()); 1162 1163 ContentValues values = new ContentValues(); 1164 values.put(COLUMN_KEY_NAME, "key10"); 1165 values.put(COLUMN_VALUE_NAME, 10); 1166 mContentResolver.update(TABLE1_URI, values, null, null); 1167 new PollingCheck() { 1168 @Override 1169 protected boolean check() { 1170 return mco.hadOnChanged(); 1171 } 1172 }.run(); 1173 1174 mco.reset(); 1175 mContentResolver.unregisterContentObserver(mco); 1176 assertFalse(mco.hadOnChanged()); 1177 mContentResolver.update(TABLE1_URI, values, null, null); 1178 1179 assertFalse(mco.hadOnChanged()); 1180 1181 try { 1182 mContentResolver.registerContentObserver(null, false, mco); 1183 fail("did not throw NullPointerException or IllegalArgumentException when uri is null."); 1184 } catch (NullPointerException e) { 1185 //expected. 1186 } catch (IllegalArgumentException e) { 1187 // also expected 1188 } 1189 1190 try { 1191 mContentResolver.registerContentObserver(TABLE1_URI, false, null); 1192 fail("did not throw NullPointerException when register null content observer."); 1193 } catch (NullPointerException e) { 1194 //expected. 1195 } 1196 1197 try { 1198 mContentResolver.unregisterContentObserver(null); 1199 fail("did not throw NullPointerException when unregister null content observer."); 1200 } catch (NullPointerException e) { 1201 //expected. 1202 } 1203 } 1204 // Tests registerContentObserverForAllUsers without INTERACT_ACROSS_USERS_FULL: verify 1205 // SecurityException. testRegisterContentObserverForAllUsersWithoutPermission()1206 public void testRegisterContentObserverForAllUsersWithoutPermission() { 1207 final MockContentObserver mco = new MockContentObserver(); 1208 try { 1209 mContentResolver.registerContentObserverAsUser(TABLE1_URI, true, mco, UserHandle.ALL); 1210 fail("testRegisterContentObserverForAllUsers: " 1211 + "SecurityException expected on testRegisterContentObserverForAllUsers"); 1212 } catch (SecurityException se) { 1213 // expected 1214 } 1215 } 1216 testRegisterContentObserverAsUser()1217 public void testRegisterContentObserverAsUser() { 1218 final MockContentObserver mco = new MockContentObserver(); 1219 1220 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( 1221 mContentResolver, 1222 (cr) -> cr.registerContentObserverAsUser(TABLE1_URI, true, mco, mContext.getUser()) 1223 ); 1224 assertFalse(mco.hadOnChanged()); 1225 1226 ContentValues values = new ContentValues(); 1227 values.put(COLUMN_KEY_NAME, "key10"); 1228 values.put(COLUMN_VALUE_NAME, 10); 1229 mContentResolver.update(TABLE1_URI, values, null, null); 1230 new PollingCheck() { 1231 @Override 1232 protected boolean check() { 1233 return mco.hadOnChanged(); 1234 } 1235 }.run(); 1236 1237 mco.reset(); 1238 mContentResolver.unregisterContentObserver(mco); 1239 assertFalse(mco.hadOnChanged()); 1240 mContentResolver.update(TABLE1_URI, values, null, null); 1241 1242 assertFalse(mco.hadOnChanged()); 1243 } 1244 testRegisterContentObserverForAllUsers()1245 public void testRegisterContentObserverForAllUsers() { 1246 final MockContentObserver mco = new MockContentObserver(); 1247 1248 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( 1249 mContentResolver, 1250 (cr) -> cr.registerContentObserverAsUser(TABLE1_URI, true, mco, UserHandle.ALL) 1251 ); 1252 assertFalse(mco.hadOnChanged()); 1253 1254 ContentValues values = new ContentValues(); 1255 values.put(COLUMN_KEY_NAME, "key10"); 1256 values.put(COLUMN_VALUE_NAME, 10); 1257 mContentResolver.update(TABLE1_URI, values, null, null); 1258 new PollingCheck() { 1259 @Override 1260 protected boolean check() { 1261 return mco.hadOnChanged(); 1262 } 1263 }.run(); 1264 1265 mco.reset(); 1266 mContentResolver.unregisterContentObserver(mco); 1267 assertFalse(mco.hadOnChanged()); 1268 mContentResolver.update(TABLE1_URI, values, null, null); 1269 1270 assertFalse(mco.hadOnChanged()); 1271 1272 try { 1273 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( 1274 mContentResolver, 1275 (cr) -> cr.registerContentObserverAsUser(null, false, mco, UserHandle.ALL) 1276 ); 1277 fail("did not throw NullPointerException or IllegalArgumentException when uri is null" 1278 + "."); 1279 } catch (NullPointerException e) { 1280 //expected. 1281 } catch (IllegalArgumentException e) { 1282 // also expected 1283 } 1284 1285 try { 1286 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( 1287 mContentResolver, 1288 (cr) -> cr.registerContentObserverAsUser(TABLE1_URI, false, null, 1289 UserHandle.ALL) 1290 ); 1291 fail("did not throw NullPointerException when register null content observer."); 1292 } catch (NullPointerException e) { 1293 //expected. 1294 } 1295 1296 try { 1297 mContentResolver.unregisterContentObserver(null); 1298 fail("did not throw NullPointerException when unregister null content observer."); 1299 } catch (NullPointerException e) { 1300 //expected. 1301 } 1302 } 1303 testRegisterContentObserverDescendantBehavior()1304 public void testRegisterContentObserverDescendantBehavior() throws Exception { 1305 final MockContentObserver mco1 = new MockContentObserver(); 1306 final MockContentObserver mco2 = new MockContentObserver(); 1307 1308 // Register one content observer with notifyDescendants set to false, and 1309 // another with true. 1310 mContentResolver.registerContentObserver(LEVEL2_URI, false, mco1); 1311 mContentResolver.registerContentObserver(LEVEL2_URI, true, mco2); 1312 1313 // Initially nothing has happened. 1314 assertFalse(mco1.hadOnChanged()); 1315 assertFalse(mco2.hadOnChanged()); 1316 1317 // Fire a change with the exact URI. 1318 // Should signal both observers due to exact match, notifyDescendants doesn't matter. 1319 mContentResolver.notifyChange(LEVEL2_URI, null); 1320 Thread.sleep(200); 1321 assertTrue(mco1.hadOnChanged()); 1322 assertTrue(mco2.hadOnChanged()); 1323 mco1.reset(); 1324 mco2.reset(); 1325 1326 // Fire a change with a descendant URI. 1327 // Should only signal observer with notifyDescendants set to true. 1328 mContentResolver.notifyChange(LEVEL3_URI, null); 1329 Thread.sleep(200); 1330 assertFalse(mco1.hadOnChanged()); 1331 assertTrue(mco2.hadOnChanged()); 1332 mco2.reset(); 1333 1334 // Fire a change with an ancestor URI. 1335 // Should signal both observers due to ancestry, notifyDescendants doesn't matter. 1336 mContentResolver.notifyChange(LEVEL1_URI, null); 1337 Thread.sleep(200); 1338 assertTrue(mco1.hadOnChanged()); 1339 assertTrue(mco2.hadOnChanged()); 1340 mco1.reset(); 1341 mco2.reset(); 1342 1343 // Fire a change with an unrelated URI. 1344 // Should signal neither observer. 1345 mContentResolver.notifyChange(TABLE1_URI, null); 1346 Thread.sleep(200); 1347 assertFalse(mco1.hadOnChanged()); 1348 assertFalse(mco2.hadOnChanged()); 1349 } 1350 testRegisterContentObserverForAllUsersDescendantBehavior()1351 public void testRegisterContentObserverForAllUsersDescendantBehavior() throws Exception { 1352 final MockContentObserver mco1 = new MockContentObserver(); 1353 final MockContentObserver mco2 = new MockContentObserver(); 1354 1355 // Register one content observer with notifyDescendants set to false, and 1356 // another with true. 1357 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( 1358 mContentResolver, 1359 (cr) -> cr.registerContentObserverAsUser(LEVEL2_URI, false, mco1, UserHandle.ALL) 1360 ); 1361 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( 1362 mContentResolver, 1363 (cr) -> cr.registerContentObserverAsUser(LEVEL2_URI, true, mco2, UserHandle.ALL) 1364 ); 1365 1366 // Initially nothing has happened. 1367 assertFalse(mco1.hadOnChanged()); 1368 assertFalse(mco2.hadOnChanged()); 1369 1370 // Fire a change with the exact URI. 1371 // Should signal both observers due to exact match, notifyDescendants doesn't matter. 1372 mContentResolver.notifyChange(LEVEL2_URI, null); 1373 Thread.sleep(200); 1374 assertTrue(mco1.hadOnChanged()); 1375 assertTrue(mco2.hadOnChanged()); 1376 mco1.reset(); 1377 mco2.reset(); 1378 1379 // Fire a change with a descendant URI. 1380 // Should only signal observer with notifyDescendants set to true. 1381 mContentResolver.notifyChange(LEVEL3_URI, null); 1382 Thread.sleep(200); 1383 assertFalse(mco1.hadOnChanged()); 1384 assertTrue(mco2.hadOnChanged()); 1385 mco2.reset(); 1386 1387 // Fire a change with an ancestor URI. 1388 // Should signal both observers due to ancestry, notifyDescendants doesn't matter. 1389 mContentResolver.notifyChange(LEVEL1_URI, null); 1390 Thread.sleep(200); 1391 assertTrue(mco1.hadOnChanged()); 1392 assertTrue(mco2.hadOnChanged()); 1393 mco1.reset(); 1394 mco2.reset(); 1395 1396 // Fire a change with an unrelated URI. 1397 // Should signal neither observer. 1398 mContentResolver.notifyChange(TABLE1_URI, null); 1399 Thread.sleep(200); 1400 assertFalse(mco1.hadOnChanged()); 1401 assertFalse(mco2.hadOnChanged()); 1402 } 1403 testNotifyChange1()1404 public void testNotifyChange1() { 1405 final MockContentObserver mco = new MockContentObserver(); 1406 1407 mContentResolver.registerContentObserver(TABLE1_URI, true, mco); 1408 assertFalse(mco.hadOnChanged()); 1409 1410 mContentResolver.notifyChange(TABLE1_URI, mco); 1411 new PollingCheck() { 1412 @Override 1413 protected boolean check() { 1414 return mco.hadOnChanged(); 1415 } 1416 }.run(); 1417 1418 mContentResolver.unregisterContentObserver(mco); 1419 } 1420 testNotifyChange2()1421 public void testNotifyChange2() { 1422 final MockContentObserver mco = new MockContentObserver(); 1423 1424 mContentResolver.registerContentObserver(TABLE1_URI, true, mco); 1425 assertFalse(mco.hadOnChanged()); 1426 1427 mContentResolver.notifyChange(TABLE1_URI, mco, false); 1428 new PollingCheck() { 1429 @Override 1430 protected boolean check() { 1431 return mco.hadOnChanged(); 1432 } 1433 }.run(); 1434 1435 mContentResolver.unregisterContentObserver(mco); 1436 } 1437 1438 /** 1439 * Verify that callers using the {@link Iterable} version of 1440 * {@link ContentResolver#notifyChange} are correctly split and delivered to 1441 * disjoint listeners. 1442 */ testNotifyChange_MultipleSplit()1443 public void testNotifyChange_MultipleSplit() { 1444 final MockContentObserver observer1 = new MockContentObserver(); 1445 final MockContentObserver observer2 = new MockContentObserver(); 1446 1447 mContentResolver.registerContentObserver(TABLE1_URI, true, observer1); 1448 mContentResolver.registerContentObserver(TABLE2_URI, true, observer2); 1449 1450 assertFalse(observer1.hadOnChanged()); 1451 assertFalse(observer2.hadOnChanged()); 1452 1453 final ArrayList<Uri> list = new ArrayList<>(); 1454 list.add(TABLE1_URI); 1455 list.add(TABLE2_URI); 1456 mContentResolver.notifyChange(list, null, 0); 1457 1458 new PollingCheck() { 1459 @Override 1460 protected boolean check() { 1461 return observer1.hadOnChanged() && observer2.hadOnChanged(); 1462 } 1463 }.run(); 1464 1465 mContentResolver.unregisterContentObserver(observer1); 1466 mContentResolver.unregisterContentObserver(observer2); 1467 } 1468 1469 /** 1470 * Verify that callers using the {@link Iterable} version of 1471 * {@link ContentResolver#notifyChange} are correctly grouped and delivered 1472 * to overlapping listeners, including untouched flags. 1473 */ testNotifyChange_MultipleFlags()1474 public void testNotifyChange_MultipleFlags() { 1475 final MockContentObserver observer1 = new MockContentObserver(); 1476 final MockContentObserver observer2 = new MockContentObserver(); 1477 1478 mContentResolver.registerContentObserver(LEVEL1_URI, false, observer1); 1479 mContentResolver.registerContentObserver(LEVEL2_URI, false, observer2); 1480 1481 mContentResolver.notifyChange( 1482 Arrays.asList(LEVEL1_URI), null, 0); 1483 mContentResolver.notifyChange( 1484 Arrays.asList(LEVEL1_URI, LEVEL2_URI), null, NOTIFY_INSERT); 1485 mContentResolver.notifyChange( 1486 Arrays.asList(LEVEL2_URI), null, NOTIFY_UPDATE); 1487 1488 final List<Change> expected1 = Arrays.asList( 1489 new Change(false, Arrays.asList(LEVEL1_URI), 0), 1490 new Change(false, Arrays.asList(LEVEL1_URI), NOTIFY_INSERT)); 1491 1492 final List<Change> expected2 = Arrays.asList( 1493 new Change(false, Arrays.asList(LEVEL1_URI), 0), 1494 new Change(false, Arrays.asList(LEVEL1_URI, LEVEL2_URI), NOTIFY_INSERT), 1495 new Change(false, Arrays.asList(LEVEL2_URI), NOTIFY_UPDATE)); 1496 1497 new PollingCheck() { 1498 @Override 1499 protected boolean check() { 1500 return observer1.hadChanges(expected1) 1501 && observer2.hadChanges(expected2); 1502 } 1503 }.run(); 1504 1505 mContentResolver.unregisterContentObserver(observer1); 1506 mContentResolver.unregisterContentObserver(observer2); 1507 } 1508 testStartCancelSync()1509 public void testStartCancelSync() { 1510 Bundle extras = new Bundle(); 1511 1512 extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); 1513 1514 ContentResolver.requestSync(ACCOUNT, AUTHORITY, extras); 1515 //FIXME: how to get the result to assert. 1516 1517 ContentResolver.cancelSync(ACCOUNT, AUTHORITY); 1518 //FIXME: how to assert. 1519 } 1520 testStartSyncFailure()1521 public void testStartSyncFailure() { 1522 try { 1523 ContentResolver.requestSync(null, null, null); 1524 fail("did not throw IllegalArgumentException when extras is null."); 1525 } catch (IllegalArgumentException e) { 1526 //expected. 1527 } 1528 } 1529 testValidateSyncExtrasBundle()1530 public void testValidateSyncExtrasBundle() { 1531 Bundle extras = new Bundle(); 1532 extras.putInt("Integer", 20); 1533 extras.putLong("Long", 10l); 1534 extras.putBoolean("Boolean", true); 1535 extras.putFloat("Float", 5.5f); 1536 extras.putDouble("Double", 2.5); 1537 extras.putString("String", "cts"); 1538 extras.putCharSequence("CharSequence", null); 1539 1540 ContentResolver.validateSyncExtrasBundle(extras); 1541 1542 extras.putChar("Char", 'a'); // type Char is invalid 1543 try { 1544 ContentResolver.validateSyncExtrasBundle(extras); 1545 fail("did not throw IllegalArgumentException when extras is invalide."); 1546 } catch (IllegalArgumentException e) { 1547 //expected. 1548 } 1549 } 1550 1551 @AppModeFull testHangRecover()1552 public void testHangRecover() throws Exception { 1553 InstrumentationRegistry.getInstrumentation().getUiAutomation() 1554 .adoptShellPermissionIdentity(android.Manifest.permission.REMOVE_TASKS); 1555 1556 final CountDownLatch latch = new CountDownLatch(1); 1557 new Thread(() -> { 1558 final ContentProviderClient client = mContentResolver 1559 .acquireUnstableContentProviderClient(REMOTE_AUTHORITY); 1560 client.setDetectNotResponding(2_000); 1561 try { 1562 client.query(REMOTE_HANG_URI, null, null, null); 1563 fail("Funky, we somehow returned?"); 1564 } catch (RemoteException e) { 1565 latch.countDown(); 1566 } 1567 }).start(); 1568 1569 // The remote process should have been killed after the ANR was detected 1570 // above, causing our pending call to return and release our latch above 1571 // within 10 seconds; if our Binder thread hasn't been freed, then we 1572 // fail with a timeout. 1573 latch.await(10, TimeUnit.SECONDS); 1574 } 1575 testGetTypeInfo()1576 public void testGetTypeInfo() throws Exception { 1577 for (String mimeType : new String[] { 1578 "image/png", 1579 "IMage/PnG", 1580 "image/x-custom", 1581 "application/x-flac", 1582 "application/rdf+xml", 1583 "x-custom/x-custom", 1584 }) { 1585 final MimeTypeInfo ti = mContentResolver.getTypeInfo(mimeType); 1586 assertNotNull(ti); 1587 assertNotNull(ti.getLabel()); 1588 assertNotNull(ti.getContentDescription()); 1589 assertNotNull(ti.getIcon()); 1590 } 1591 } 1592 testGetTypeInfo_Invalid()1593 public void testGetTypeInfo_Invalid() throws Exception { 1594 try { 1595 mContentResolver.getTypeInfo(null); 1596 fail("Expected exception for null"); 1597 } catch (NullPointerException expected) { 1598 } 1599 } 1600 testWrapContentProvider()1601 public void testWrapContentProvider() throws Exception { 1602 try (ContentProviderClient local = getContext().getContentResolver() 1603 .acquireContentProviderClient(AUTHORITY)) { 1604 final ContentResolver resolver = ContentResolver.wrap(local.getLocalContentProvider()); 1605 assertNotNull(resolver.getType(TABLE1_URI)); 1606 try { 1607 resolver.getType(REMOTE_TABLE1_URI); 1608 fail(); 1609 } catch (SecurityException | IllegalArgumentException expected) { 1610 } 1611 } 1612 } 1613 testWrapContentProviderClient()1614 public void testWrapContentProviderClient() throws Exception { 1615 try (ContentProviderClient remote = getContext().getContentResolver() 1616 .acquireContentProviderClient(REMOTE_AUTHORITY)) { 1617 final ContentResolver resolver = ContentResolver.wrap(remote); 1618 assertNotNull(resolver.getType(REMOTE_TABLE1_URI)); 1619 try { 1620 resolver.getType(TABLE1_URI); 1621 fail(); 1622 } catch (SecurityException | IllegalArgumentException expected) { 1623 } 1624 } 1625 } 1626 1627 @AppModeFull testContentResolverCaching()1628 public void testContentResolverCaching() throws Exception { 1629 InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( 1630 android.Manifest.permission.CACHE_CONTENT, 1631 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); 1632 1633 Bundle cached = new Bundle(); 1634 cached.putString("key", "value"); 1635 mContentResolver.putCache(TABLE1_URI, cached); 1636 1637 Bundle response = mContentResolver.getCache(TABLE1_URI); 1638 assertEquals("value", response.getString("key")); 1639 1640 ContentValues values = new ContentValues(); 1641 values.put(COLUMN_KEY_NAME, "key10"); 1642 values.put(COLUMN_VALUE_NAME, 10); 1643 mContentResolver.update(TABLE1_URI, values, null, null); 1644 1645 response = mContentResolver.getCache(TABLE1_URI); 1646 assertNull(response); 1647 } 1648 testEncodeDecode()1649 public void testEncodeDecode() { 1650 final Uri expected = Uri.parse("content://com.example/item/23"); 1651 final File file = ContentResolver.encodeToFile(expected); 1652 assertNotNull(file); 1653 1654 final Uri actual = ContentResolver.decodeFromFile(file); 1655 assertNotNull(actual); 1656 assertEquals(expected, actual); 1657 } 1658 1659 public static class Change { 1660 public final boolean selfChange; 1661 public final Iterable<Uri> uris; 1662 public final int flags; 1663 @UserIdInt 1664 public final int userId; 1665 Change(boolean selfChange, Iterable<Uri> uris, int flags)1666 public Change(boolean selfChange, Iterable<Uri> uris, int flags) { 1667 this.selfChange = selfChange; 1668 this.uris = uris; 1669 this.flags = flags; 1670 this.userId = -1; 1671 } 1672 Change(boolean selfChange, Iterable<Uri> uris, int flags, @UserIdInt int userId)1673 public Change(boolean selfChange, Iterable<Uri> uris, int flags, @UserIdInt int userId) { 1674 this.selfChange = selfChange; 1675 this.uris = uris; 1676 this.flags = flags; 1677 this.userId = userId; 1678 } 1679 1680 @Override toString()1681 public String toString() { 1682 return String.format("onChange(%b, %s, %d, %d)", 1683 selfChange, asSet(uris).toString(), flags, userId); 1684 } 1685 1686 @Override equals(Object other)1687 public boolean equals(Object other) { 1688 if (other instanceof Change) { 1689 final Change change = (Change) other; 1690 return change.selfChange == selfChange && 1691 Objects.equals(asSet(change.uris), asSet(uris)) && 1692 change.flags == flags && change.userId == userId; 1693 } else { 1694 return false; 1695 } 1696 } 1697 asSet(Iterable<Uri> uris)1698 private static Set<Uri> asSet(Iterable<Uri> uris) { 1699 final Set<Uri> asSet = new HashSet<>(); 1700 uris.forEach(asSet::add); 1701 return asSet; 1702 } 1703 } 1704 1705 private static class MockContentObserver extends ContentObserver { 1706 private boolean mHadOnChanged = false; 1707 private List<Change> mChanges = new ArrayList<>(); 1708 MockContentObserver()1709 public MockContentObserver() { 1710 super(null); 1711 } 1712 1713 @Override deliverSelfNotifications()1714 public boolean deliverSelfNotifications() { 1715 return true; 1716 } 1717 1718 @Override onChange(boolean selfChange, Collection<Uri> uris, int flags)1719 public synchronized void onChange(boolean selfChange, Collection<Uri> uris, int flags) { 1720 doOnChangeLocked(selfChange, uris, flags, /*userId=*/ -1); 1721 } 1722 1723 @Override onChange(boolean selfChange, @NonNull Collection<Uri> uris, @ContentResolver.NotifyFlags int flags, UserHandle user)1724 public synchronized void onChange(boolean selfChange, @NonNull Collection<Uri> uris, 1725 @ContentResolver.NotifyFlags int flags, UserHandle user) { 1726 doOnChangeLocked(selfChange, uris, flags, user.getIdentifier()); 1727 } 1728 hadOnChanged()1729 public synchronized boolean hadOnChanged() { 1730 return mHadOnChanged; 1731 } 1732 reset()1733 public synchronized void reset() { 1734 mHadOnChanged = false; 1735 } 1736 hadChanges(Collection<Change> changes)1737 public synchronized boolean hadChanges(Collection<Change> changes) { 1738 return mChanges.containsAll(changes); 1739 } 1740 1741 @GuardedBy("this") doOnChangeLocked(boolean selfChange, @NonNull Collection<Uri> uris, @ContentResolver.NotifyFlags int flags, @UserIdInt int userId)1742 private void doOnChangeLocked(boolean selfChange, @NonNull Collection<Uri> uris, 1743 @ContentResolver.NotifyFlags int flags, @UserIdInt int userId) { 1744 final Change change = new Change(selfChange, uris, flags, userId); 1745 Log.v(TAG, change.toString()); 1746 1747 mHadOnChanged = true; 1748 mChanges.add(change); 1749 } 1750 } 1751 } 1752