1 /* 2 * Copyright (C) 2019 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 org.mockito.Mockito.RETURNS_DEFAULTS; 20 import static org.mockito.Mockito.after; 21 import static org.mockito.Mockito.doAnswer; 22 import static org.mockito.Mockito.doReturn; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.spy; 26 import static org.mockito.Mockito.verify; 27 28 import android.content.AttributionSource; 29 import android.content.ContentProvider; 30 import android.content.ContentProviderClient; 31 import android.content.ContentProviderOperation; 32 import android.content.ContentResolver; 33 import android.content.ContentValues; 34 import android.content.Context; 35 import android.content.ContextParams; 36 import android.content.IContentProvider; 37 import android.content.OperationApplicationException; 38 import android.net.Uri; 39 import android.os.Bundle; 40 import android.os.CancellationSignal; 41 import android.os.ICancellationSignal; 42 import android.os.OperationCanceledException; 43 import android.os.RemoteException; 44 import android.test.AndroidTestCase; 45 import android.test.mock.MockContentResolver; 46 47 import org.mockito.stubbing.Answer; 48 49 import java.io.FileNotFoundException; 50 import java.util.ArrayList; 51 import java.util.concurrent.CountDownLatch; 52 import java.util.concurrent.TimeUnit; 53 54 /** 55 * Simple delegation test for {@link ContentProviderClient}, checking the right methods are called. 56 * Actual {@link ContentProvider} functionality is tested in {@link ContentProviderTest}. 57 */ 58 public class ContentProviderClientTest extends AndroidTestCase { 59 60 private static final Answer ANSWER_SLEEP = invocation -> { 61 // Sleep long enough to trigger ANR 62 Thread.sleep(100); 63 return null; 64 }; 65 66 private static final String PACKAGE_NAME = "android.content.cts"; 67 private static final String FEATURE_ID = "testFeature"; 68 private static final String MODE = "mode"; 69 private static final String AUTHORITY = "authority"; 70 private static final String METHOD = "method"; 71 private static final String ARG = "arg"; 72 private static final Uri URI = Uri.parse("com.example.app://path"); 73 private static final Bundle ARGS = new Bundle(); 74 private static final Bundle EXTRAS = new Bundle(); 75 private static final ContentValues VALUES = new ContentValues(); 76 private static final ContentValues[] VALUES_ARRAY = {VALUES}; 77 private static final ArrayList<ContentProviderOperation> OPS = new ArrayList<>(); 78 79 private ContentResolver mContentResolver; 80 private IContentProvider mIContentProvider; 81 private ContentProviderClient mContentProviderClient; 82 private AttributionSource mAttributionSource; 83 84 private CancellationSignal mCancellationSignal = new CancellationSignal(); 85 private ICancellationSignal mICancellationSignal; 86 private boolean mCalledCancel = false; 87 88 @Override setUp()89 public void setUp() throws Exception { 90 super.setUp(); 91 92 mIContentProvider = mock(IContentProvider.class, RETURNS_DEFAULTS); 93 mICancellationSignal = mock(ICancellationSignal.class); 94 95 doReturn(mICancellationSignal).when(mIContentProvider).createCancellationSignal(); 96 97 final Context attributionContext = getContext().createContext( 98 new ContextParams.Builder() 99 .setAttributionTag(FEATURE_ID) 100 .build()); 101 102 mAttributionSource = attributionContext.getAttributionSource(); 103 mContentResolver = spy(new MockContentResolver(attributionContext)); 104 mContentProviderClient = spy(new ContentProviderClient(mContentResolver, mIContentProvider, 105 false)); 106 107 mCalledCancel = false; 108 } 109 110 @Override tearDown()111 protected void tearDown() throws Exception { 112 super.tearDown(); 113 if (!mCalledCancel) { 114 // Client should never cancel unless the test called cancel 115 assertFalse(mCancellationSignal.isCanceled()); 116 verify(mICancellationSignal, never()).cancel(); 117 } 118 } 119 testQuery()120 public void testQuery() throws RemoteException { 121 mContentProviderClient.query(URI, null, ARGS, mCancellationSignal); 122 verify(mIContentProvider).query(mAttributionSource, URI, null, ARGS, 123 mICancellationSignal); 124 } 125 testQueryTimeout()126 public void testQueryTimeout() throws RemoteException, InterruptedException { 127 doAnswer(ANSWER_SLEEP).when(mIContentProvider).query(mAttributionSource, URI, null, 128 ARGS, mICancellationSignal); 129 130 testTimeout(() -> mContentProviderClient.query(URI, null, ARGS, mCancellationSignal)); 131 132 verify(mIContentProvider, after(150)).query(mAttributionSource, URI, null, ARGS, 133 mICancellationSignal); 134 } 135 testQueryAlreadyCancelled()136 public void testQueryAlreadyCancelled() throws Exception { 137 testAlreadyCancelled( 138 () -> mContentProviderClient.query(URI, null, ARGS, mCancellationSignal)); 139 verify(mIContentProvider, never()).query(mAttributionSource, URI, null, ARGS, 140 mICancellationSignal); 141 } 142 testGetType()143 public void testGetType() throws RemoteException { 144 mContentProviderClient.getType(URI); 145 verify(mIContentProvider).getType(mAttributionSource, URI); 146 } 147 testGetTypeTimeout()148 public void testGetTypeTimeout() throws RemoteException, InterruptedException { 149 doAnswer(ANSWER_SLEEP).when(mIContentProvider).getType(mAttributionSource, URI); 150 151 testTimeout(() -> mContentProviderClient.getType(URI)); 152 153 verify(mIContentProvider, after(150)).getType(mAttributionSource, URI); 154 } 155 testGetStreamTypes()156 public void testGetStreamTypes() throws RemoteException { 157 mContentProviderClient.getStreamTypes(URI, ""); 158 verify(mIContentProvider).getStreamTypes(mAttributionSource, URI, ""); 159 } 160 testGetStreamTypesTimeout()161 public void testGetStreamTypesTimeout() throws RemoteException, InterruptedException { 162 doAnswer(ANSWER_SLEEP).when(mIContentProvider).getStreamTypes(mAttributionSource, 163 URI, ""); 164 165 testTimeout(() -> mContentProviderClient.getStreamTypes(URI, "")); 166 167 verify(mIContentProvider, after(150)).getStreamTypes(mAttributionSource, URI, ""); 168 } 169 testCanonicalize()170 public void testCanonicalize() throws RemoteException { 171 mContentProviderClient.canonicalize(URI); 172 verify(mIContentProvider).canonicalize(mAttributionSource, URI); 173 } 174 testCanonicalizeTimeout()175 public void testCanonicalizeTimeout() throws RemoteException, InterruptedException { 176 doAnswer(ANSWER_SLEEP).when(mIContentProvider).canonicalize(mAttributionSource, URI); 177 178 testTimeout(() -> mContentProviderClient.canonicalize(URI)); 179 180 verify(mIContentProvider, after(150)).canonicalize(mAttributionSource, URI); 181 } 182 testUncanonicalize()183 public void testUncanonicalize() throws RemoteException { 184 mContentProviderClient.uncanonicalize(URI); 185 verify(mIContentProvider).uncanonicalize(mAttributionSource, URI); 186 } 187 testUncanonicalizeTimeout()188 public void testUncanonicalizeTimeout() throws RemoteException, InterruptedException { 189 doAnswer(ANSWER_SLEEP).when(mIContentProvider).uncanonicalize(mAttributionSource, URI); 190 191 testTimeout(() -> mContentProviderClient.uncanonicalize(URI)); 192 193 verify(mIContentProvider, after(150)).uncanonicalize(mAttributionSource, URI); 194 } 195 testRefresh()196 public void testRefresh() throws RemoteException { 197 mContentProviderClient.refresh(URI, ARGS, mCancellationSignal); 198 verify(mIContentProvider).refresh(mAttributionSource, URI, ARGS, 199 mICancellationSignal); 200 } 201 testRefreshTimeout()202 public void testRefreshTimeout() throws RemoteException, InterruptedException { 203 doAnswer(ANSWER_SLEEP).when(mIContentProvider).refresh(mAttributionSource, URI, ARGS, 204 mICancellationSignal); 205 206 testTimeout(() -> mContentProviderClient.refresh(URI, ARGS, mCancellationSignal)); 207 208 verify(mIContentProvider, after(150)).refresh(mAttributionSource, URI, ARGS, 209 mICancellationSignal); 210 } 211 testRefreshAlreadyCancelled()212 public void testRefreshAlreadyCancelled() throws Exception { 213 testAlreadyCancelled(() -> mContentProviderClient.refresh(URI, ARGS, mCancellationSignal)); 214 verify(mIContentProvider, never()).refresh(mAttributionSource, URI, ARGS, 215 mICancellationSignal); 216 } 217 testInsert()218 public void testInsert() throws RemoteException { 219 mContentProviderClient.insert(URI, VALUES, EXTRAS); 220 verify(mIContentProvider).insert(mAttributionSource, URI, VALUES, EXTRAS); 221 } 222 testInsertTimeout()223 public void testInsertTimeout() throws RemoteException, InterruptedException { 224 doAnswer(ANSWER_SLEEP).when(mIContentProvider).insert(mAttributionSource, URI, 225 VALUES, EXTRAS); 226 227 testTimeout(() -> mContentProviderClient.insert(URI, VALUES, EXTRAS)); 228 229 verify(mIContentProvider, after(150)).insert(mAttributionSource, URI, VALUES, EXTRAS); 230 } 231 testBulkInsert()232 public void testBulkInsert() throws RemoteException { 233 mContentProviderClient.bulkInsert(URI, VALUES_ARRAY); 234 verify(mIContentProvider).bulkInsert(mAttributionSource, URI, VALUES_ARRAY); 235 } 236 testBulkInsertTimeout()237 public void testBulkInsertTimeout() throws RemoteException, InterruptedException { 238 doAnswer(ANSWER_SLEEP).when(mIContentProvider).bulkInsert(mAttributionSource, URI, 239 VALUES_ARRAY); 240 241 testTimeout(() -> mContentProviderClient.bulkInsert(URI, VALUES_ARRAY)); 242 243 verify(mIContentProvider, after(150)).bulkInsert(mAttributionSource, URI, VALUES_ARRAY); 244 } 245 testDelete()246 public void testDelete() throws RemoteException { 247 mContentProviderClient.delete(URI, EXTRAS); 248 verify(mIContentProvider).delete(mAttributionSource, URI, EXTRAS); 249 } 250 testDeleteTimeout()251 public void testDeleteTimeout() throws RemoteException, InterruptedException { 252 doAnswer(ANSWER_SLEEP).when(mIContentProvider).delete(mAttributionSource, URI, EXTRAS); 253 254 testTimeout(() -> mContentProviderClient.delete(URI, EXTRAS)); 255 256 verify(mIContentProvider, after(150)).delete(mAttributionSource, URI, EXTRAS); 257 } 258 testUpdate()259 public void testUpdate() throws RemoteException { 260 mContentProviderClient.update(URI, VALUES, EXTRAS); 261 verify(mIContentProvider).update(mAttributionSource, URI, VALUES, EXTRAS); 262 } 263 testUpdateTimeout()264 public void testUpdateTimeout() throws RemoteException, InterruptedException { 265 doAnswer(ANSWER_SLEEP).when(mIContentProvider).update(mAttributionSource, URI, 266 VALUES, EXTRAS); 267 268 testTimeout(() -> mContentProviderClient.update(URI, VALUES, EXTRAS)); 269 270 verify(mIContentProvider, after(150)).update(mAttributionSource, URI, VALUES, EXTRAS); 271 } 272 testOpenFile()273 public void testOpenFile() throws RemoteException, FileNotFoundException { 274 mContentProviderClient.openFile(URI, MODE, mCancellationSignal); 275 276 verify(mIContentProvider).openFile(mAttributionSource, URI, MODE, mICancellationSignal); 277 } 278 testOpenFileTimeout()279 public void testOpenFileTimeout() 280 throws RemoteException, InterruptedException, FileNotFoundException { 281 doAnswer(ANSWER_SLEEP).when(mIContentProvider).openFile(mAttributionSource, 282 URI, MODE, mICancellationSignal); 283 284 testTimeout(() -> mContentProviderClient.openFile(URI, MODE, mCancellationSignal)); 285 286 verify(mIContentProvider, after(150)).openFile(mAttributionSource, URI, MODE, 287 mICancellationSignal); 288 } 289 testOpenFileAlreadyCancelled()290 public void testOpenFileAlreadyCancelled() throws Exception { 291 testAlreadyCancelled(() -> mContentProviderClient.openFile(URI, MODE, mCancellationSignal)); 292 293 verify(mIContentProvider, never()).openFile(mAttributionSource, URI, MODE, 294 mICancellationSignal); 295 } 296 testOpenAssetFile()297 public void testOpenAssetFile() throws RemoteException, FileNotFoundException { 298 mContentProviderClient.openAssetFile(URI, MODE, mCancellationSignal); 299 300 verify(mIContentProvider).openAssetFile(mAttributionSource, URI, MODE, mICancellationSignal); 301 } 302 testOpenAssetFileTimeout()303 public void testOpenAssetFileTimeout() 304 throws RemoteException, InterruptedException, FileNotFoundException { 305 doAnswer(ANSWER_SLEEP).when(mIContentProvider).openAssetFile(mAttributionSource, 306 URI, MODE, mICancellationSignal); 307 308 testTimeout(() -> mContentProviderClient.openAssetFile(URI, MODE, mCancellationSignal)); 309 310 verify(mIContentProvider, after(150)).openAssetFile(mAttributionSource, URI, MODE, 311 mICancellationSignal); 312 } 313 testOpenAssetFileAlreadyCancelled()314 public void testOpenAssetFileAlreadyCancelled() throws Exception { 315 testAlreadyCancelled( 316 () -> mContentProviderClient.openAssetFile(URI, MODE, mCancellationSignal)); 317 318 verify(mIContentProvider, never()).openAssetFile(mAttributionSource, URI, MODE, 319 mICancellationSignal); 320 } 321 testOpenTypedAssetFileDescriptor()322 public void testOpenTypedAssetFileDescriptor() throws RemoteException, FileNotFoundException { 323 mContentProviderClient.openTypedAssetFileDescriptor(URI, MODE, ARGS, mCancellationSignal); 324 325 verify(mIContentProvider).openTypedAssetFile(mAttributionSource, URI, MODE, ARGS, 326 mICancellationSignal); 327 } 328 testOpenTypedAssetFile()329 public void testOpenTypedAssetFile() throws RemoteException, FileNotFoundException { 330 mContentProviderClient.openTypedAssetFile(URI, MODE, ARGS, mCancellationSignal); 331 332 verify(mIContentProvider).openTypedAssetFile(mAttributionSource, URI, MODE, ARGS, 333 mICancellationSignal); 334 } 335 testOpenTypedAssetFileTimeout()336 public void testOpenTypedAssetFileTimeout() 337 throws RemoteException, InterruptedException, FileNotFoundException { 338 doAnswer(ANSWER_SLEEP).when(mIContentProvider).openTypedAssetFile(mAttributionSource, 339 URI, MODE, ARGS, mICancellationSignal); 340 341 testTimeout(() -> mContentProviderClient.openTypedAssetFile(URI, MODE, ARGS, 342 mCancellationSignal)); 343 344 verify(mIContentProvider, after(150)).openTypedAssetFile(mAttributionSource, URI, MODE, 345 ARGS, mICancellationSignal); 346 } 347 testOpenTypedAssetFileAlreadyCancelled()348 public void testOpenTypedAssetFileAlreadyCancelled() throws Exception { 349 testAlreadyCancelled( 350 () -> mContentProviderClient.openTypedAssetFile(URI, MODE, ARGS, 351 mCancellationSignal)); 352 353 verify(mIContentProvider, never()).openTypedAssetFile(mAttributionSource, URI, MODE, 354 ARGS, mICancellationSignal); 355 } 356 testApplyBatch()357 public void testApplyBatch() throws RemoteException, OperationApplicationException { 358 mContentProviderClient.applyBatch(AUTHORITY, OPS); 359 360 verify(mIContentProvider).applyBatch(mAttributionSource, AUTHORITY, OPS); 361 } 362 testApplyBatchTimeout()363 public void testApplyBatchTimeout() 364 throws RemoteException, InterruptedException, OperationApplicationException { 365 doAnswer(ANSWER_SLEEP).when(mIContentProvider).applyBatch(mAttributionSource, 366 AUTHORITY, OPS); 367 368 testTimeout(() -> mContentProviderClient.applyBatch(AUTHORITY, OPS)); 369 370 verify(mIContentProvider, after(150)).applyBatch(mAttributionSource, AUTHORITY, OPS); 371 } 372 testCall()373 public void testCall() throws RemoteException { 374 mContentProviderClient.call(AUTHORITY, METHOD, ARG, ARGS); 375 376 verify(mIContentProvider).call(mAttributionSource, AUTHORITY, METHOD, ARG, ARGS); 377 } 378 testCallTimeout()379 public void testCallTimeout() throws RemoteException, InterruptedException { 380 doAnswer(ANSWER_SLEEP).when(mIContentProvider).call(mAttributionSource, AUTHORITY, 381 METHOD, ARG, ARGS); 382 383 testTimeout(() -> mContentProviderClient.call(AUTHORITY, METHOD, ARG, ARGS)); 384 385 verify(mIContentProvider, after(150)).call(mAttributionSource, AUTHORITY, METHOD, ARG, 386 ARGS); 387 } 388 testTimeout(Function function)389 private void testTimeout(Function function) throws InterruptedException { 390 mContentProviderClient.setDetectNotResponding(1); 391 CountDownLatch latch = new CountDownLatch(1); 392 doAnswer(invocation -> { 393 latch.countDown(); 394 return null; 395 }) 396 .when(mContentResolver) 397 .appNotRespondingViaProvider(mIContentProvider); 398 399 new Thread(() -> { 400 try { 401 function.run(); 402 } catch (Exception ignored) { 403 } finally { 404 latch.countDown(); 405 } 406 }).start(); 407 408 latch.await(100, TimeUnit.MILLISECONDS); 409 assertEquals(0, latch.getCount()); 410 411 verify(mContentResolver).appNotRespondingViaProvider(mIContentProvider); 412 } 413 testAlreadyCancelled(Function function)414 private void testAlreadyCancelled(Function function) throws Exception { 415 mCancellationSignal.cancel(); 416 mCalledCancel = true; 417 418 try { 419 function.run(); 420 fail("Expected OperationCanceledException"); 421 } catch (OperationCanceledException expected) { 422 } 423 } 424 425 private interface Function { run()426 void run() throws Exception; 427 } 428 } 429