diff --git a/networksecurity/service/src/com/android/server/net/ct/DownloadHelper.java b/networksecurity/service/src/com/android/server/net/ct/DownloadHelper.java index 57484168c1..4087775bd8 100644 --- a/networksecurity/service/src/com/android/server/net/ct/DownloadHelper.java +++ b/networksecurity/service/src/com/android/server/net/ct/DownloadHelper.java @@ -25,6 +25,7 @@ import android.net.Uri; import androidx.annotation.VisibleForTesting; import com.google.auto.value.AutoValue; +import java.util.Objects; /** Class to handle downloads for Certificate Transparency. */ public class DownloadHelper { @@ -47,6 +48,10 @@ public class DownloadHelper { * @return a downloadId if the request was created successfully, -1 otherwise. */ public long startDownload(String url) { + long downloadId = isUrlAlreadyScheduled(url); + if (downloadId != -1) { + return downloadId; + } return mDownloadManager.enqueue( new Request(Uri.parse(url)) .setAllowedOverRoaming(false) @@ -54,6 +59,33 @@ public class DownloadHelper { .setRequiresCharging(true)); } + private long isUrlAlreadyScheduled(String url) { + try (Cursor cursor = + mDownloadManager.query( + new Query() + .setFilterByStatus( + DownloadManager.STATUS_PENDING + | DownloadManager.STATUS_RUNNING + | DownloadManager.STATUS_PAUSED))) { + if (cursor == null) { + return -1; + } + for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { + int idIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID); + int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_URI); + if (idIndex == -1 || uriIndex == -1) { + continue; + } + long downloadId = cursor.getLong(idIndex); + String uri = cursor.getString(uriIndex); + if (Objects.equals(uri, url)) { + return downloadId; + } + } + } + return -1; + } + /** * Returns the status of the provided download id. * @@ -67,7 +99,7 @@ public class DownloadHelper { builder.setStatus( cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))); builder.setReason( - cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON))); + cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON))); } } return builder.build(); diff --git a/networksecurity/tests/unit/src/com/android/server/net/ct/DownloadHelperTest.java b/networksecurity/tests/unit/src/com/android/server/net/ct/DownloadHelperTest.java index 0b65e3cb8d..0db9604329 100644 --- a/networksecurity/tests/unit/src/com/android/server/net/ct/DownloadHelperTest.java +++ b/networksecurity/tests/unit/src/com/android/server/net/ct/DownloadHelperTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.when; import android.app.DownloadManager; +import android.database.MatrixCursor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -53,6 +54,21 @@ public class DownloadHelperTest { assertThat(mDownloadHelper.startDownload("http://test.org")).isEqualTo(downloadId); } + @Test + public void testDownloadHelper_multipleDownloads_doesNotRescheduleDuplicates() { + long downloadId1 = 666; + String url1 = "http://test1.org"; + long downloadId2 = 777; + String url2 = "http://test2.org"; + when(mDownloadManager.enqueue(any())).thenReturn(downloadId1, downloadId2); + + assertThat(mDownloadHelper.startDownload(url1)).isEqualTo(downloadId1); + mockPendingDownload(downloadId1, url1); + + assertThat(mDownloadHelper.startDownload(url2)).isEqualTo(downloadId2); + assertThat(mDownloadHelper.startDownload(url1)).isEqualTo(downloadId1); + } + @Test public void testDownloadHelper_wrongUri() { when(mDownloadManager.enqueue(any())).thenReturn(666L); @@ -60,4 +76,17 @@ public class DownloadHelperTest { assertThrows( IllegalArgumentException.class, () -> mDownloadHelper.startDownload("not_a_uri")); } + + private void mockPendingDownload(long downloadId, String url) { + MatrixCursor cursor = + new MatrixCursor( + new String[] { + DownloadManager.COLUMN_ID, + DownloadManager.COLUMN_URI, + DownloadManager.COLUMN_STATUS + }); + cursor.addRow(new Object[] {downloadId, url, DownloadManager.STATUS_PENDING}); + + when(mDownloadManager.query(any())).thenReturn(cursor); + } }