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