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