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