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.doAnswer; 20 import static org.mockito.Mockito.doReturn; 21 import static org.mockito.Mockito.mock; 22 import static org.mockito.Mockito.never; 23 import static org.mockito.Mockito.spy; 24 import static org.mockito.Mockito.verify; 25 import static org.mockito.Mockito.RETURNS_DEFAULTS; 26 27 import android.content.AttributionSource; 28 import android.content.ContentProvider; 29 import android.content.ContentProviderClient; 30 import android.content.ContentProviderOperation; 31 import android.content.ContentResolver; 32 import android.content.ContentValues; 33 import android.content.Context; 34 import android.content.ContextParams; 35 import android.content.IContentProvider; 36 import android.content.OperationApplicationException; 37 import android.net.Uri; 38 import android.os.Bundle; 39 import android.os.CancellationSignal; 40 import android.os.ICancellationSignal; 41 import android.os.OperationCanceledException; 42 import android.os.Process; 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).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(URI); 146 } 147 testGetTypeTimeout()148 public void testGetTypeTimeout() throws RemoteException, InterruptedException { 149 doAnswer(ANSWER_SLEEP).when(mIContentProvider).getType(URI); 150 151 testTimeout(() -> mContentProviderClient.getType(URI)); 152 153 verify(mIContentProvider).getType(URI); 154 } 155 testGetStreamTypes()156 public void testGetStreamTypes() throws RemoteException { 157 mContentProviderClient.getStreamTypes(URI, ""); 158 verify(mIContentProvider).getStreamTypes(URI, ""); 159 } 160 testGetStreamTypesTimeout()161 public void testGetStreamTypesTimeout() throws RemoteException, InterruptedException { 162 doAnswer(ANSWER_SLEEP).when(mIContentProvider).getStreamTypes(URI, ""); 163 164 testTimeout(() -> mContentProviderClient.getStreamTypes(URI, "")); 165 166 verify(mIContentProvider).getStreamTypes(URI, ""); 167 } 168 testCanonicalize()169 public void testCanonicalize() throws RemoteException { 170 mContentProviderClient.canonicalize(URI); 171 verify(mIContentProvider).canonicalize(mAttributionSource, URI); 172 } 173 testCanonicalizeTimeout()174 public void testCanonicalizeTimeout() throws RemoteException, InterruptedException { 175 doAnswer(ANSWER_SLEEP).when(mIContentProvider).canonicalize(mAttributionSource, URI); 176 177 testTimeout(() -> mContentProviderClient.canonicalize(URI)); 178 179 verify(mIContentProvider).canonicalize(mAttributionSource, URI); 180 } 181 testUncanonicalize()182 public void testUncanonicalize() throws RemoteException { 183 mContentProviderClient.uncanonicalize(URI); 184 verify(mIContentProvider).uncanonicalize(mAttributionSource, URI); 185 } 186 testUncanonicalizeTimeout()187 public void testUncanonicalizeTimeout() throws RemoteException, InterruptedException { 188 doAnswer(ANSWER_SLEEP).when(mIContentProvider).uncanonicalize(mAttributionSource, URI); 189 190 testTimeout(() -> mContentProviderClient.uncanonicalize(URI)); 191 192 verify(mIContentProvider).uncanonicalize(mAttributionSource, URI); 193 } 194 testRefresh()195 public void testRefresh() throws RemoteException { 196 mContentProviderClient.refresh(URI, ARGS, mCancellationSignal); 197 verify(mIContentProvider).refresh(mAttributionSource, URI, ARGS, 198 mICancellationSignal); 199 } 200 testRefreshTimeout()201 public void testRefreshTimeout() throws RemoteException, InterruptedException { 202 doAnswer(ANSWER_SLEEP).when(mIContentProvider).refresh(mAttributionSource, URI, ARGS, 203 mICancellationSignal); 204 205 testTimeout(() -> mContentProviderClient.refresh(URI, ARGS, mCancellationSignal)); 206 207 verify(mIContentProvider).refresh(mAttributionSource, URI, ARGS, 208 mICancellationSignal); 209 } 210 testRefreshAlreadyCancelled()211 public void testRefreshAlreadyCancelled() throws Exception { 212 testAlreadyCancelled(() -> mContentProviderClient.refresh(URI, ARGS, mCancellationSignal)); 213 verify(mIContentProvider, never()).refresh(mAttributionSource, URI, ARGS, 214 mICancellationSignal); 215 } 216 testInsert()217 public void testInsert() throws RemoteException { 218 mContentProviderClient.insert(URI, VALUES, EXTRAS); 219 verify(mIContentProvider).insert(mAttributionSource, URI, VALUES, EXTRAS); 220 } 221 testInsertTimeout()222 public void testInsertTimeout() throws RemoteException, InterruptedException { 223 doAnswer(ANSWER_SLEEP).when(mIContentProvider).insert(mAttributionSource, URI, 224 VALUES, EXTRAS); 225 226 testTimeout(() -> mContentProviderClient.insert(URI, VALUES, EXTRAS)); 227 228 verify(mIContentProvider).insert(mAttributionSource, URI, VALUES, EXTRAS); 229 } 230 testBulkInsert()231 public void testBulkInsert() throws RemoteException { 232 mContentProviderClient.bulkInsert(URI, VALUES_ARRAY); 233 verify(mIContentProvider).bulkInsert(mAttributionSource, URI, VALUES_ARRAY); 234 } 235 testBulkInsertTimeout()236 public void testBulkInsertTimeout() throws RemoteException, InterruptedException { 237 doAnswer(ANSWER_SLEEP).when(mIContentProvider).bulkInsert(mAttributionSource, URI, 238 VALUES_ARRAY); 239 240 testTimeout(() -> mContentProviderClient.bulkInsert(URI, VALUES_ARRAY)); 241 242 verify(mIContentProvider).bulkInsert(mAttributionSource, URI, VALUES_ARRAY); 243 } 244 testDelete()245 public void testDelete() throws RemoteException { 246 mContentProviderClient.delete(URI, EXTRAS); 247 verify(mIContentProvider).delete(mAttributionSource, URI, EXTRAS); 248 } 249 testDeleteTimeout()250 public void testDeleteTimeout() throws RemoteException, InterruptedException { 251 doAnswer(ANSWER_SLEEP).when(mIContentProvider).delete(mAttributionSource, URI, EXTRAS); 252 253 testTimeout(() -> mContentProviderClient.delete(URI, EXTRAS)); 254 255 verify(mIContentProvider).delete(mAttributionSource, URI, EXTRAS); 256 } 257 testUpdate()258 public void testUpdate() throws RemoteException { 259 mContentProviderClient.update(URI, VALUES, EXTRAS); 260 verify(mIContentProvider).update(mAttributionSource, URI, VALUES, EXTRAS); 261 } 262 testUpdateTimeout()263 public void testUpdateTimeout() throws RemoteException, InterruptedException { 264 doAnswer(ANSWER_SLEEP).when(mIContentProvider).update(mAttributionSource, URI, 265 VALUES, EXTRAS); 266 267 testTimeout(() -> mContentProviderClient.update(URI, VALUES, EXTRAS)); 268 269 verify(mIContentProvider).update(mAttributionSource, URI, VALUES, EXTRAS); 270 } 271 testOpenFile()272 public void testOpenFile() throws RemoteException, FileNotFoundException { 273 mContentProviderClient.openFile(URI, MODE, mCancellationSignal); 274 275 verify(mIContentProvider).openFile(mAttributionSource, URI, MODE, mICancellationSignal); 276 } 277 testOpenFileTimeout()278 public void testOpenFileTimeout() 279 throws RemoteException, InterruptedException, FileNotFoundException { 280 doAnswer(ANSWER_SLEEP).when(mIContentProvider).openFile(mAttributionSource, 281 URI, MODE, mICancellationSignal); 282 283 testTimeout(() -> mContentProviderClient.openFile(URI, MODE, mCancellationSignal)); 284 285 verify(mIContentProvider).openFile(mAttributionSource, URI, MODE, mICancellationSignal); 286 } 287 testOpenFileAlreadyCancelled()288 public void testOpenFileAlreadyCancelled() throws Exception { 289 testAlreadyCancelled(() -> mContentProviderClient.openFile(URI, MODE, mCancellationSignal)); 290 291 verify(mIContentProvider, never()).openFile(mAttributionSource, URI, MODE, 292 mICancellationSignal); 293 } 294 testOpenAssetFile()295 public void testOpenAssetFile() throws RemoteException, FileNotFoundException { 296 mContentProviderClient.openAssetFile(URI, MODE, mCancellationSignal); 297 298 verify(mIContentProvider).openAssetFile(mAttributionSource, URI, MODE, mICancellationSignal); 299 } 300 testOpenAssetFileTimeout()301 public void testOpenAssetFileTimeout() 302 throws RemoteException, InterruptedException, FileNotFoundException { 303 doAnswer(ANSWER_SLEEP).when(mIContentProvider).openAssetFile(mAttributionSource, 304 URI, MODE, mICancellationSignal); 305 306 testTimeout(() -> mContentProviderClient.openAssetFile(URI, MODE, mCancellationSignal)); 307 308 verify(mIContentProvider).openAssetFile(mAttributionSource, URI, MODE, 309 mICancellationSignal); 310 } 311 testOpenAssetFileAlreadyCancelled()312 public void testOpenAssetFileAlreadyCancelled() throws Exception { 313 testAlreadyCancelled( 314 () -> mContentProviderClient.openAssetFile(URI, MODE, mCancellationSignal)); 315 316 verify(mIContentProvider, never()).openAssetFile(mAttributionSource, URI, MODE, 317 mICancellationSignal); 318 } 319 testOpenTypedAssetFileDescriptor()320 public void testOpenTypedAssetFileDescriptor() throws RemoteException, FileNotFoundException { 321 mContentProviderClient.openTypedAssetFileDescriptor(URI, MODE, ARGS, mCancellationSignal); 322 323 verify(mIContentProvider).openTypedAssetFile(mAttributionSource, URI, MODE, ARGS, 324 mICancellationSignal); 325 } 326 testOpenTypedAssetFile()327 public void testOpenTypedAssetFile() throws RemoteException, FileNotFoundException { 328 mContentProviderClient.openTypedAssetFile(URI, MODE, ARGS, mCancellationSignal); 329 330 verify(mIContentProvider).openTypedAssetFile(mAttributionSource, URI, MODE, ARGS, 331 mICancellationSignal); 332 } 333 testOpenTypedAssetFileTimeout()334 public void testOpenTypedAssetFileTimeout() 335 throws RemoteException, InterruptedException, FileNotFoundException { 336 doAnswer(ANSWER_SLEEP).when(mIContentProvider).openTypedAssetFile(mAttributionSource, 337 URI, MODE, ARGS, mICancellationSignal); 338 339 testTimeout(() -> mContentProviderClient.openTypedAssetFile(URI, MODE, ARGS, 340 mCancellationSignal)); 341 342 verify(mIContentProvider).openTypedAssetFile(mAttributionSource, URI, MODE, ARGS, 343 mICancellationSignal); 344 } 345 testOpenTypedAssetFileAlreadyCancelled()346 public void testOpenTypedAssetFileAlreadyCancelled() throws Exception { 347 testAlreadyCancelled( 348 () -> mContentProviderClient.openTypedAssetFile(URI, MODE, ARGS, 349 mCancellationSignal)); 350 351 verify(mIContentProvider, never()).openTypedAssetFile(mAttributionSource, URI, MODE, 352 ARGS, mICancellationSignal); 353 } 354 testApplyBatch()355 public void testApplyBatch() throws RemoteException, OperationApplicationException { 356 mContentProviderClient.applyBatch(AUTHORITY, OPS); 357 358 verify(mIContentProvider).applyBatch(mAttributionSource, AUTHORITY, OPS); 359 } 360 testApplyBatchTimeout()361 public void testApplyBatchTimeout() 362 throws RemoteException, InterruptedException, OperationApplicationException { 363 doAnswer(ANSWER_SLEEP).when(mIContentProvider).applyBatch(mAttributionSource, 364 AUTHORITY, OPS); 365 366 testTimeout(() -> mContentProviderClient.applyBatch(AUTHORITY, OPS)); 367 368 verify(mIContentProvider).applyBatch(mAttributionSource, AUTHORITY, OPS); 369 } 370 testCall()371 public void testCall() throws RemoteException { 372 mContentProviderClient.call(AUTHORITY, METHOD, ARG, ARGS); 373 374 verify(mIContentProvider).call(mAttributionSource, AUTHORITY, METHOD, ARG, ARGS); 375 } 376 testCallTimeout()377 public void testCallTimeout() throws RemoteException, InterruptedException { 378 doAnswer(ANSWER_SLEEP).when(mIContentProvider).call(mAttributionSource, AUTHORITY, 379 METHOD, ARG, ARGS); 380 381 testTimeout(() -> mContentProviderClient.call(AUTHORITY, METHOD, ARG, ARGS)); 382 383 verify(mIContentProvider).call(mAttributionSource, AUTHORITY, METHOD, ARG, ARGS); 384 } 385 testTimeout(Function function)386 private void testTimeout(Function function) throws InterruptedException { 387 mContentProviderClient.setDetectNotResponding(1); 388 CountDownLatch latch = new CountDownLatch(1); 389 doAnswer(invocation -> { 390 latch.countDown(); 391 return null; 392 }) 393 .when(mContentResolver) 394 .appNotRespondingViaProvider(mIContentProvider); 395 396 new Thread(() -> { 397 try { 398 function.run(); 399 } catch (Exception ignored) { 400 } finally { 401 latch.countDown(); 402 } 403 }).start(); 404 405 latch.await(100, TimeUnit.MILLISECONDS); 406 assertEquals(0, latch.getCount()); 407 408 verify(mContentResolver).appNotRespondingViaProvider(mIContentProvider); 409 } 410 testAlreadyCancelled(Function function)411 private void testAlreadyCancelled(Function function) throws Exception { 412 mCancellationSignal.cancel(); 413 mCalledCancel = true; 414 415 try { 416 function.run(); 417 fail("Expected OperationCanceledException"); 418 } catch (OperationCanceledException expected) { 419 } 420 } 421 422 private interface Function { run()423 void run() throws Exception; 424 } 425 } 426