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