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