• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 com.android.providers.downloads;
18 
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.when;
21 
22 import android.app.DownloadManager;
23 import android.app.NotificationManager;
24 import android.app.job.JobParameters;
25 import android.app.job.JobScheduler;
26 import android.content.ContentResolver;
27 import android.content.ContentValues;
28 import android.content.Context;
29 import android.content.pm.ProviderInfo;
30 import android.database.ContentObserver;
31 import android.database.Cursor;
32 import android.database.DatabaseUtils;
33 import android.database.MatrixCursor;
34 import android.net.Uri;
35 import android.os.Binder;
36 import android.os.Bundle;
37 import android.os.IBinder;
38 import android.provider.Downloads;
39 import android.provider.MediaStore;
40 import android.test.MoreAsserts;
41 import android.test.RenamingDelegatingContext;
42 import android.test.ServiceTestCase;
43 import android.test.mock.MockContentProvider;
44 import android.test.mock.MockContentResolver;
45 import android.util.Log;
46 
47 import com.google.mockwebserver.MockResponse;
48 import com.google.mockwebserver.MockWebServer;
49 import com.google.mockwebserver.RecordedRequest;
50 import com.google.mockwebserver.SocketPolicy;
51 
52 import java.io.BufferedReader;
53 import java.io.File;
54 import java.io.IOException;
55 import java.io.InputStream;
56 import java.io.InputStreamReader;
57 import java.net.MalformedURLException;
58 import java.net.UnknownHostException;
59 
60 public abstract class AbstractDownloadProviderFunctionalTest extends
61         ServiceTestCase<DownloadJobService> {
62 
63     protected static final String LOG_TAG = "DownloadProviderFunctionalTest";
64     private static final String PROVIDER_AUTHORITY = "downloads";
65     protected static final long RETRY_DELAY_MILLIS = 61 * 1000;
66 
67     protected static final String
68             FILE_CONTENT = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
69 
70     private final MockitoHelper mMockitoHelper = new MockitoHelper();
71 
72     protected MockWebServer mServer;
73     protected MockContentResolverWithNotify mResolver;
74     protected TestContext mTestContext;
75     protected FakeSystemFacade mSystemFacade;
76     protected static String STRING_1K;
77     static {
78         StringBuilder buff = new StringBuilder();
79         for (int i = 0; i < 1024; i++) {
80             buff.append("a" + i % 26);
81         }
82         STRING_1K = buff.toString();
83     }
84 
85     static class MockContentResolverWithNotify extends MockContentResolver {
86         public boolean mNotifyWasCalled = false;
87 
MockContentResolverWithNotify(Context context)88         public MockContentResolverWithNotify(Context context) {
89             super(context);
90         }
91 
resetNotified()92         public synchronized void resetNotified() {
93             mNotifyWasCalled = false;
94         }
95 
96         @Override
notifyChange( Uri uri, ContentObserver observer)97         public synchronized void notifyChange(
98                 Uri uri, ContentObserver observer) {
99             mNotifyWasCalled = true;
100         }
101     }
102 
103     static class MockMediaProvider extends MockContentProvider {
104         private static final Uri TEST_URI = Uri.parse("content://media/external/11111111");
105         @Override
delete(Uri uri, String selection, String[] selectionArgs)106         public int delete(Uri uri, String selection, String[] selectionArgs) {
107             return 0;
108         }
109 
110         @Override
insert(Uri uri, ContentValues values)111         public Uri insert(Uri uri, ContentValues values) {
112             return TEST_URI;
113         }
114 
115         @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)116         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
117                 String sortOrder) {
118             return new MatrixCursor(new String[0], 0);
119         }
120 
121         @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)122         public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
123             return 1;
124         }
125 
126         @Override
call(String method, String request, Bundle args)127         public Bundle call(String method, String request, Bundle args) {
128           return new Bundle();
129         }
130     }
131 
132     /**
133      * Context passed to the provider and the service.  Allows most methods to pass through to the
134      * real Context (this is a LargeTest), with a few exceptions, including renaming file operations
135      * to avoid file and DB conflicts (via RenamingDelegatingContext).
136      */
137     static class TestContext extends RenamingDelegatingContext {
138         private static final String FILENAME_PREFIX = "test.";
139 
140         private final ContentResolver mResolver;
141         private final NotificationManager mNotifManager;
142         private final DownloadManager mDownloadManager;
143         private final JobScheduler mJobScheduler;
144 
TestContext(Context realContext)145         public TestContext(Context realContext) {
146             super(realContext, FILENAME_PREFIX);
147             mResolver = new MockContentResolverWithNotify(this);
148             mNotifManager = mock(NotificationManager.class);
149             mDownloadManager = mock(DownloadManager.class);
150             mJobScheduler = mock(JobScheduler.class);
151         }
152 
153         /**
154          * Direct DownloadService to our test instance of DownloadProvider.
155          */
156         @Override
getContentResolver()157         public ContentResolver getContentResolver() {
158             return mResolver;
159         }
160 
161         /**
162          * Stub some system services, allow access to others, and block the rest.
163          */
164         @Override
getSystemService(String name)165         public Object getSystemService(String name) {
166             if (Context.NOTIFICATION_SERVICE.equals(name)) {
167                 return mNotifManager;
168             } else if (Context.DOWNLOAD_SERVICE.equals(name)) {
169                 return mDownloadManager;
170             } else if (Context.JOB_SCHEDULER_SERVICE.equals(name)) {
171                 return mJobScheduler;
172             }
173 
174             return super.getSystemService(name);
175         }
176     }
177 
AbstractDownloadProviderFunctionalTest(FakeSystemFacade systemFacade)178     public AbstractDownloadProviderFunctionalTest(FakeSystemFacade systemFacade) {
179         super(DownloadJobService.class);
180         mSystemFacade = systemFacade;
181     }
182 
183     @Override
setUp()184     protected void setUp() throws Exception {
185         super.setUp();
186         mMockitoHelper.setUp(getClass());
187 
188         // Since we're testing a system app, AppDataDirGuesser doesn't find our
189         // cache dir, so set it explicitly.
190         System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
191 
192         final Context realContext = getContext();
193 
194         mTestContext = new TestContext(realContext);
195         mResolver = (MockContentResolverWithNotify) mTestContext.getContentResolver();
196 
197         final DownloadProvider provider = new DownloadProvider();
198         provider.mSystemFacade = mSystemFacade;
199 
200         ProviderInfo info = new ProviderInfo();
201         info.authority = "downloads";
202         provider.attachInfo(mTestContext, info);
203 
204         mResolver.addProvider(PROVIDER_AUTHORITY, provider);
205         mResolver.addProvider(MediaStore.AUTHORITY, new MockMediaProvider());
206 
207         setContext(mTestContext);
208         setupService();
209         Helpers.setSystemFacade(mSystemFacade);
210 
211         cleanUpDownloads();
212         mSystemFacade.setUp();
213         assertDatabaseEmpty(); // ensure we're not messing with real data
214         assertDatabaseSecureAgainstBadSelection();
215         mServer = new MockWebServer();
216         mServer.play();
217     }
218 
219     @Override
tearDown()220     protected void tearDown() throws Exception {
221         cleanUpDownloads();
222         mServer.shutdown();
223         mMockitoHelper.tearDown();
224         super.tearDown();
225     }
226 
startDownload(long id)227     protected void startDownload(long id) {
228         final JobParameters params = mock(JobParameters.class);
229         when(params.getJobId()).thenReturn((int) id);
230         getService().onBind(null);
231         getService().onStartJob(params);
232     }
233 
assertDatabaseEmpty()234     private void assertDatabaseEmpty() {
235         try (Cursor cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
236                 null, null, null, null)) {
237             assertEquals(0, cursor.getCount());
238         }
239     }
240 
assertDatabaseSecureAgainstBadSelection()241     private void assertDatabaseSecureAgainstBadSelection() {
242         try (Cursor cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, null,
243                 "('1'='1'))) ORDER BY lastmod DESC--", null, null)) {
244             fail("Database isn't secure!");
245         } catch (Exception expected) {
246         }
247     }
248 
249     /**
250      * Remove any downloaded files and delete any lingering downloads.
251      */
cleanUpDownloads()252     void cleanUpDownloads() {
253         if (mResolver == null) {
254             return;
255         }
256         String[] columns = new String[] {Downloads.Impl._DATA};
257         Cursor cursor = mResolver.query(Downloads.Impl.CONTENT_URI, columns, null, null, null);
258         try {
259             for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
260                 String filePath = cursor.getString(0);
261                 if (filePath == null) continue;
262                 Log.d(LOG_TAG, "Deleting " + filePath);
263                 new File(filePath).delete();
264             }
265         } finally {
266             cursor.close();
267         }
268         mResolver.delete(Downloads.Impl.CONTENT_URI, null, null);
269     }
270 
enqueueResponse(MockResponse resp)271     void enqueueResponse(MockResponse resp) {
272         mServer.enqueue(resp);
273     }
274 
buildResponse(int status, String body)275     MockResponse buildResponse(int status, String body) {
276         return new MockResponse().setResponseCode(status).setBody(body)
277                 .setHeader("Content-type", "text/plain")
278                 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
279     }
280 
buildResponse(int status, byte[] body)281     MockResponse buildResponse(int status, byte[] body) {
282         return new MockResponse().setResponseCode(status).setBody(body)
283                 .setHeader("Content-type", "text/plain")
284                 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
285     }
286 
buildEmptyResponse(int status)287     MockResponse buildEmptyResponse(int status) {
288         return buildResponse(status, "");
289     }
290 
291     /**
292      * Fetch the last request received by the MockWebServer.
293      */
takeRequest()294     protected RecordedRequest takeRequest() throws InterruptedException {
295         RecordedRequest request = mServer.takeRequest();
296         assertNotNull("Expected request was not made", request);
297         return request;
298     }
299 
getServerUri(String path)300     String getServerUri(String path) throws MalformedURLException, UnknownHostException {
301         return mServer.getUrl(path).toString();
302     }
303 
readStream(InputStream inputStream)304     protected String readStream(InputStream inputStream) throws IOException {
305         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
306         try {
307             char[] buffer = new char[1024];
308             int length = reader.read(buffer);
309             assertTrue("Failed to read anything from input stream", length > -1);
310             return String.valueOf(buffer, 0, length);
311         } finally {
312             reader.close();
313         }
314     }
315 
assertStartsWith(String expectedPrefix, String actual)316     protected void assertStartsWith(String expectedPrefix, String actual) {
317         String regex = "^" + expectedPrefix + ".*";
318         MoreAsserts.assertMatchesRegex(regex, actual);
319     }
320 }
321