From 5ba69740a47857fcebd36866e07963ba798269d5 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 1 Mar 2013 11:18:38 -0800 Subject: Fix race conditions around filename claiming. When multiple downloads are running in parallel, they can end up claiming the same filename and clobber over each other. This change introduces locking around filename generation, and touches the claimed filename so other threads fail the File.exists() check and keep looking. Tests to verify. Bug: 8255596 Change-Id: Ie75ed047c199cf679832c75159056ca036eac18d --- .../android/providers/downloads/ThreadingTest.java | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'tests/src') diff --git a/tests/src/com/android/providers/downloads/ThreadingTest.java b/tests/src/com/android/providers/downloads/ThreadingTest.java index fcb33290..920f703b 100644 --- a/tests/src/com/android/providers/downloads/ThreadingTest.java +++ b/tests/src/com/android/providers/downloads/ThreadingTest.java @@ -20,6 +20,15 @@ import static java.net.HttpURLConnection.HTTP_OK; import android.app.DownloadManager; import android.test.suitebuilder.annotation.LargeTest; +import android.util.Pair; + +import com.google.android.collect.Lists; +import com.google.android.collect.Sets; +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.SocketPolicy; + +import java.util.List; +import java.util.Set; /** * Download manager tests that require multithreading. @@ -48,4 +57,41 @@ public class ThreadingTest extends AbstractPublicApiTest { Thread.sleep(10); } } + + public void testFilenameRace() throws Exception { + final List> downloads = Lists.newArrayList(); + + // Request dozen files at once with same name + for (int i = 0; i < 12; i++) { + final String body = "DOWNLOAD " + i + " CONTENTS"; + enqueueResponse(new MockResponse().setResponseCode(HTTP_OK).setBody(body) + .setHeader("Content-type", "text/plain") + .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END)); + + final Download d = enqueueRequest(getRequest()); + downloads.add(Pair.create(d, body)); + } + + // Kick off downloads in parallel + final long startMillis = mSystemFacade.currentTimeMillis(); + startService(null); + + for (Pair d : downloads) { + d.first.waitForStatus(DownloadManager.STATUS_SUCCESSFUL, startMillis); + } + + // Ensure that contents are clean and filenames unique + final Set seenFiles = Sets.newHashSet(); + + for (Pair d : downloads) { + final String file = d.first.getStringField(DownloadManager.COLUMN_LOCAL_FILENAME); + if (!seenFiles.add(file)) { + fail("Another download already claimed " + file); + } + + final String expected = d.second; + final String actual = d.first.getContents(); + assertEquals(expected, actual); + } + } } -- cgit v1.2.3