summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2017-12-06 11:52:11 -0800
committerXin Li <delphij@google.com>2017-12-06 14:24:54 -0800
commit799550d5779056949cd79998cab3926fc1b8b50f (patch)
tree4a910777062c89c8187a89d98c55bc1c413a4bb9
parentb50329927cf915576474ce4a3d9a7ff9e57c4df5 (diff)
parent4acb87f8a9b2bb3e9fbea9666681d8fe174c7570 (diff)
downloadandroid_packages_providers_DownloadProvider-799550d5779056949cd79998cab3926fc1b8b50f.tar.gz
android_packages_providers_DownloadProvider-799550d5779056949cd79998cab3926fc1b8b50f.tar.bz2
android_packages_providers_DownloadProvider-799550d5779056949cd79998cab3926fc1b8b50f.zip
DO NOT MERGE: Merge Oreo MR1 into master
Exempt-From-Owner-Approval: Changes already landed internally Change-Id: Ie9c6ce7cbe644e94a16e183d1d9d59cd1a3554f2
-rw-r--r--AndroidManifest.xml1
-rw-r--r--res/values-fi/strings.xml2
-rw-r--r--res/values-hi/strings.xml2
-rw-r--r--res/values-ru/strings.xml4
-rw-r--r--res/values-sv/strings.xml8
-rw-r--r--res/values-ta/strings.xml2
-rw-r--r--src/com/android/providers/downloads/DownloadInfo.java7
-rw-r--r--src/com/android/providers/downloads/DownloadNotifier.java10
-rw-r--r--src/com/android/providers/downloads/DownloadProvider.java14
-rw-r--r--src/com/android/providers/downloads/DownloadStorageProvider.java8
-rw-r--r--src/com/android/providers/downloads/DownloadThread.java51
-rw-r--r--src/com/android/providers/downloads/Helpers.java8
-rw-r--r--src/com/android/providers/downloads/RealSystemFacade.java7
-rw-r--r--src/com/android/providers/downloads/StorageUtils.java170
-rw-r--r--src/com/android/providers/downloads/SystemFacade.java3
-rw-r--r--tests/AndroidManifest.xml2
-rw-r--r--tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java35
-rw-r--r--tests/src/com/android/providers/downloads/FakeSystemFacade.java9
-rw-r--r--tests/src/com/android/providers/downloads/StorageTest.java249
-rw-r--r--ui/res/values-el/strings.xml2
-rw-r--r--ui/res/values-hi/strings.xml2
-rw-r--r--ui/res/values-iw/strings.xml2
-rw-r--r--ui/res/values-sv/strings.xml2
23 files changed, 71 insertions, 529 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f802f056..067bc937 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -53,7 +53,6 @@
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<!-- TODO: replace with READ_NETWORK_POLICY permission when it exists -->
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
- <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index b2ce59f8..dbe1a53e 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -34,7 +34,7 @@
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Käytä kaikkia järjestelmän latauksia"</string>
<string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Antaa sovelluksen tarkastella ja muokata kaikkia minkä tahansa järjestelmän sovelluksen käynnistämiä latauksia."</string>
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;Nimetön&gt;"</string>
- <string name="notification_download_complete" msgid="5443563299253103667">"Lataus valmis."</string>
+ <string name="notification_download_complete" msgid="5443563299253103667">"Lataus valmis"</string>
<string name="notification_download_failed" msgid="8612136111952014978">"Lataus epäonnistui."</string>
<string name="notification_need_wifi_for_size" msgid="2556172885154833575">"Lataa Wi-Fi-yhteydellä"</string>
<string name="notification_paused_in_background" msgid="4328508073283591772">"Keskeytetty taustalla."</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 0449936c..bdc6f6a7 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -21,7 +21,7 @@
<string name="permlab_downloadManager" msgid="7779544811202855500">"डाउनलोड प्रबंधक में पहुंच प्राप्त करें."</string>
<string name="permdesc_downloadManager" msgid="4237406545998908947">"ऐप को डाउनलोड मैनेजर तक पहुंचने देता है और फ़ाइलें डाउनलोड करने के लिए इसका इस्तेमाल करने देता है. धोखा देने वाले ऐप इसका इस्तेमाल डाउनलोड को रोकने और निजी जानकारी तक पहुंचने के लिए कर सकते हैं."</string>
<string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"बेहतर डाउनलोड प्रबंधक फ़क्शन."</string>
- <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"एप को डाउनलोड मैनेजर के बेहतर फ़ंक्शन तक पहुंचने देता है. धोखा देने वाले ऐप इसका इस्तेमाल डाउनलोड को रोकने और निजी जानकारी तक पहुंचने के लिए कर सकते हैं."</string>
+ <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"एेप्लिकेशन को डाउनलोड मैनेजर के बेहतर फ़ंक्शन के इस्तेमाल की मंज़ूरी देता है. धोखा देने वाले एेप्लिकेशन इसका इस्तेमाल डाउनलोड को रोकने और निजी जानकारी तक पहुंचने के लिए कर सकते हैं."</string>
<string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"डाउनलोड सूचनाएं भेजें."</string>
<string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"पूरे किए गए डाउनलोड के बारे में ऐप को सूचना भेजने देता है. धोखा देने वाले ऐप इसका इस्तेमाल फ़ाइलों को डाउनलोड करने वाले दूसरे ऐप को भ्रम में डालने के लिए कर सकते हैं."</string>
<string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"USB मेमोरी के सभी डाउनलोड देखें"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index da9756fe..a0fe2ffe 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -22,8 +22,8 @@
<string name="permdesc_downloadManager" msgid="4237406545998908947">"Приложение получит доступ к диспетчеру загрузок и сможет загружать файлы через него. Вредоносные программы смогут таким образом прерывать загрузки и просматривать личную информацию."</string>
<string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Расширенные функции менеджера загрузки."</string>
<string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Приложение получит доступ к расширенным функциям диспетчера загрузок. Вредоносные программы смогут таким образом прерывать загрузки и просматривать личную информацию."</string>
- <string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Отправить оповещение о загрузке."</string>
- <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Приложение сможет отправлять оповещения о завершенных загрузках. Вредоносные программы смогут таким образом мешать работе приложений, загружающих файлы."</string>
+ <string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Отправить уведомление о загрузке."</string>
+ <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Приложение сможет отправлять уведомления о завершенных загрузках. Вредоносные программы смогут таким образом мешать работе приложений, загружающих файлы."</string>
<string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Отслеживать все загрузки на USB-накопитель"</string>
<string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Просмотреть все загрузки на SD-карту"</string>
<string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Приложение получит доступ ко всем загрузкам на SD-карте независимо от того, через какое ПО они были загружены."</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index e6aeeb23..dce169e2 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -28,9 +28,9 @@
<string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Visa alla nedladdningar till SD-kort"</string>
<string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Tillåter att appen ser alla nedladdningar till SD-kortet, oavsett vilken app som laddat ned innehållet."</string>
<string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Reservera utrymme i nedladdningscachen"</string>
- <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Tillåter att appen hämtar filer till hämtningscachen som inte kan tas bort automatiskt när hämtningshanteraren behöver mer utrymme."</string>
+ <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Tillåter att appen laddar ned filer till hämtningscachen som inte kan tas bort automatiskt när hämtningshanteraren behöver mer utrymme."</string>
<string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"ladda ned filer utan avisering"</string>
- <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Tillåter att appen hämtar filer via nedladdningshanteraren utan att något meddelande visas för användaren."</string>
+ <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Tillåter att appen laddar ned filer via nedladdningshanteraren utan att något meddelande visas för användaren."</string>
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Åtkomst till alla nedladdningar i systemet"</string>
<string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Tillåter att appen visar och ändrar alla nedladdningar som initieras av en app i systemet."</string>
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;Okänd&gt;"</string>
@@ -40,8 +40,8 @@
<string name="notification_paused_in_background" msgid="4328508073283591772">"Pausad i bakgrunden."</string>
<string name="wifi_required_title" msgid="1995971416871498179">"Nedladdningen är för stor för operatörsnätverket"</string>
<string name="wifi_required_body" msgid="3067694630143784449">"Du måste använda Wi-Fi om du vill slutföra nedladdningen på <xliff:g id="SIZE">%s </xliff:g>. \n \n Tryck på <xliff:g id="QUEUE_TEXT">%s </xliff:g> om du vill påbörja nedladdningen nästa gång du är ansluten till ett Wi-Fi-nätverk."</string>
- <string name="wifi_recommended_title" msgid="7441589306734687400">"Vill du ställa den i kö för hämtning senare?"</string>
- <string name="wifi_recommended_body" msgid="1314735166699936073">"Om du hämtar <xliff:g id="SIZE">%s </xliff:g> kan det leda till att batteritiden minskar eller att dataanslutningen överbelastas. Det kan i sin tur innebära att operatören tar ut högre avgifter, beroende på abonnemang.\n\n Tryck på <xliff:g id="QUEUE_TEXT">%s</xliff:g> om du vill starta nedladdningen nästa gång mobilen är ansluten till ett Wi-Fi-nätverk."</string>
+ <string name="wifi_recommended_title" msgid="7441589306734687400">"Vill du ställa den i kö för nedladdning senare?"</string>
+ <string name="wifi_recommended_body" msgid="1314735166699936073">"Om du laddar ned <xliff:g id="SIZE">%s </xliff:g> kan det leda till att batteritiden minskar eller att dataanslutningen överbelastas. Det kan i sin tur innebära att operatören tar ut högre avgifter, beroende på abonnemang.\n\n Tryck på <xliff:g id="QUEUE_TEXT">%s</xliff:g> om du vill starta nedladdningen nästa gång mobilen är ansluten till ett Wi-Fi-nätverk."</string>
<string name="button_queue_for_wifi" msgid="422576726189179221">"Kö"</string>
<string name="button_cancel_download" msgid="2430166148737975604">"Avbryt"</string>
<string name="button_start_now" msgid="792123674007840864">"Starta nu"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 0932a620..13e5d2a8 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -41,7 +41,7 @@
<string name="wifi_required_title" msgid="1995971416871498179">"ஆபரேட்டர் நெட்வொர்க்கில் மிக அதிக அளவில் பதிவிறக்கப்படுகிறது"</string>
<string name="wifi_required_body" msgid="3067694630143784449">"<xliff:g id="SIZE">%s </xliff:g> பதிவிறக்கத்தை முடிக்க நீங்கள் வைஃபை ஐப் பயன்படுத்த வேண்டும். \n\nஅடுத்த முறை நீங்கள் வைஃபை நெட்வொர்க்குடன் இணைந்தவுடன் பதிவிறக்கத்தைத் தொடங்குவதற்கு <xliff:g id="QUEUE_TEXT">%s </xliff:g> ஐத் தொடவும்."</string>
<string name="wifi_recommended_title" msgid="7441589306734687400">"பின்னர் பதிவிறக்க வரிசைப்படுத்தவா?"</string>
- <string name="wifi_recommended_body" msgid="1314735166699936073">"<xliff:g id="SIZE">%s </xliff:g> அளவிலான இந்தப் பதிவிறக்கத்தை இப்போது தொடங்குவது உங்கள் பேட்டரியின் ஆயுளைக் குறைக்கலாம் மற்றும்/அல்லது உங்கள் மொபைல் டேட்டா இணைப்பை அதிகமாகப் பயன்படுத்த நேரிடலாம், இதன் காரணமாக உங்கள் தரவு திட்டத்தைப் பொறுத்து உங்கள் மொபைல் ஆபரேட்டர் கட்டணங்கள் விதிக்கலாம்.\n\nஅடுத்த முறை நீங்கள் வைஃபை நெட்வொர்க்கில் இணைந்தவுடன் இந்தப் பதிவிறக்கத்தைத் தொடங்குவதற்கு <xliff:g id="QUEUE_TEXT">%s</xliff:g> ஐத் தொடவும்."</string>
+ <string name="wifi_recommended_body" msgid="1314735166699936073">"<xliff:g id="SIZE">%s </xliff:g> அளவிலான இந்தப் பதிவிறக்கத்தை இப்போது தொடங்குவது உங்கள் பேட்டரியின் ஆயுளைக் குறைக்கலாம் மற்றும்/அல்லது உங்கள் மொபைல் டேட்டா இணைப்பை அதிகமாகப் பயன்படுத்த நேரிடலாம், இதன் காரணமாக உங்கள் தரவு திட்டத்தைப் பொறுத்து உங்கள் மொபைல் ஆபரேட்டர் பேமெண்ட்கள் விதிக்கலாம்.\n\nஅடுத்த முறை நீங்கள் வைஃபை நெட்வொர்க்கில் இணைந்தவுடன் இந்தப் பதிவிறக்கத்தைத் தொடங்குவதற்கு <xliff:g id="QUEUE_TEXT">%s</xliff:g> ஐத் தொடவும்."</string>
<string name="button_queue_for_wifi" msgid="422576726189179221">"வரிசை"</string>
<string name="button_cancel_download" msgid="2430166148737975604">"ரத்துசெய்"</string>
<string name="button_start_now" msgid="792123674007840864">"இப்போது தொடங்கு"</string>
diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java
index c571de4d..9ad7e755 100644
--- a/src/com/android/providers/downloads/DownloadInfo.java
+++ b/src/com/android/providers/downloads/DownloadInfo.java
@@ -380,13 +380,6 @@ public class DownloadInfo {
}
}
- public boolean isOnCache() {
- return (mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION
- || mDestination == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION
- || mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING
- || mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
- }
-
public Uri getMyDownloadsUri() {
return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, mId);
}
diff --git a/src/com/android/providers/downloads/DownloadNotifier.java b/src/com/android/providers/downloads/DownloadNotifier.java
index b7016fcb..d38aa755 100644
--- a/src/com/android/providers/downloads/DownloadNotifier.java
+++ b/src/com/android/providers/downloads/DownloadNotifier.java
@@ -229,6 +229,7 @@ public class DownloadNotifier {
final Uri uri = new Uri.Builder().scheme("active-dl").appendPath(tag).build();
final Intent intent = new Intent(Constants.ACTION_LIST,
uri, mContext, DownloadReceiver.class);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS,
downloadIds);
builder.setContentIntent(PendingIntent.getBroadcast(mContext,
@@ -241,6 +242,7 @@ public class DownloadNotifier {
final Uri cancelUri = new Uri.Builder().scheme("cancel-dl").appendPath(tag).build();
final Intent cancelIntent = new Intent(Constants.ACTION_CANCEL,
cancelUri, mContext, DownloadReceiver.class);
+ cancelIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
cancelIntent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_IDS, downloadIds);
cancelIntent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG, tag);
@@ -264,14 +266,11 @@ public class DownloadNotifier {
if (Downloads.Impl.isStatusError(status)) {
action = Constants.ACTION_LIST;
} else {
- if (destination != Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION) {
- action = Constants.ACTION_OPEN;
- } else {
- action = Constants.ACTION_LIST;
- }
+ action = Constants.ACTION_OPEN;
}
final Intent intent = new Intent(action, uri, mContext, DownloadReceiver.class);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS,
getDownloadIds(cursor, cluster));
builder.setContentIntent(PendingIntent.getBroadcast(mContext,
@@ -279,6 +278,7 @@ public class DownloadNotifier {
final Intent hideIntent = new Intent(Constants.ACTION_HIDE,
uri, mContext, DownloadReceiver.class);
+ hideIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
builder.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, hideIntent, 0));
}
diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java
index d50b394c..e177da17 100644
--- a/src/com/android/providers/downloads/DownloadProvider.java
+++ b/src/com/android/providers/downloads/DownloadProvider.java
@@ -85,6 +85,8 @@ public final class DownloadProvider extends ContentProvider {
private static final int DB_VERSION = 110;
/** Name of table in the database */
private static final String DB_TABLE = "downloads";
+ /** Memory optimization - close idle connections after 30s of inactivity */
+ private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
/** MIME type for the entire download list */
private static final String DOWNLOAD_LIST_TYPE = "vnd.android.cursor.dir/download";
@@ -235,6 +237,7 @@ public final class DownloadProvider extends ContentProvider {
private final class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(final Context context) {
super(context, DB_NAME, null, DB_VERSION);
+ setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
}
/**
@@ -580,8 +583,7 @@ public final class DownloadProvider extends ContentProvider {
if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)
!= PackageManager.PERMISSION_GRANTED
&& (dest == Downloads.Impl.DESTINATION_CACHE_PARTITION
- || dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING
- || dest == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION)) {
+ || dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING)) {
throw new SecurityException("setting destination to : " + dest +
" not allowed, unless PERMISSION_ACCESS_ADVANCED is granted");
}
@@ -608,12 +610,6 @@ public final class DownloadProvider extends ContentProvider {
getCallingPackage()) != AppOpsManager.MODE_ALLOWED) {
throw new SecurityException("No permission to write");
}
-
- } else if (dest == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION) {
- getContext().enforcePermission(
- android.Manifest.permission.ACCESS_CACHE_FILESYSTEM,
- Binder.getCallingPid(), Binder.getCallingUid(),
- "need ACCESS_CACHE_FILESYSTEM permission to use system cache");
}
filteredValues.put(Downloads.Impl.COLUMN_DESTINATION, dest);
}
@@ -1306,6 +1302,8 @@ public final class DownloadProvider extends ContentProvider {
try {
getContext().getContentResolver().delete(Uri.parse(mediaUri), null,
null);
+ } catch (Exception e) {
+ Log.w(Constants.TAG, "Failed to delete media entry: " + e);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java
index bbcb06d2..92f4c021 100644
--- a/src/com/android/providers/downloads/DownloadStorageProvider.java
+++ b/src/com/android/providers/downloads/DownloadStorageProvider.java
@@ -382,14 +382,6 @@ public class DownloadStorageProvider extends FileSystemProvider {
}
@Override
- public AssetFileDescriptor openDocumentThumbnail(
- String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
- // TODO: extend ExifInterface to support fds
- final ParcelFileDescriptor pfd = openDocument(docId, "r", signal);
- return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
- }
-
- @Override
protected File getFileForDocId(String docId, boolean visible) throws FileNotFoundException {
if (RawDocumentsHelper.isRawDocId(docId)) {
return new File(RawDocumentsHelper.getAbsoluteFilePath(docId));
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index e101c74d..9c920053 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -25,6 +25,7 @@ import static android.provider.Downloads.Impl.STATUS_CANCELED;
import static android.provider.Downloads.Impl.STATUS_CANNOT_RESUME;
import static android.provider.Downloads.Impl.STATUS_FILE_ERROR;
import static android.provider.Downloads.Impl.STATUS_HTTP_DATA_ERROR;
+import static android.provider.Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR;
import static android.provider.Downloads.Impl.STATUS_PAUSED_BY_APP;
import static android.provider.Downloads.Impl.STATUS_QUEUED_FOR_WIFI;
import static android.provider.Downloads.Impl.STATUS_RUNNING;
@@ -47,7 +48,6 @@ import static java.net.HttpURLConnection.HTTP_PRECON_FAILED;
import static java.net.HttpURLConnection.HTTP_SEE_OTHER;
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
-import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.content.ContentValues;
import android.content.Context;
@@ -64,6 +64,7 @@ import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
+import android.os.storage.StorageManager;
import android.provider.Downloads;
import android.system.ErrnoException;
import android.system.Os;
@@ -117,6 +118,7 @@ public class DownloadThread extends Thread {
private final SystemFacade mSystemFacade;
private final DownloadNotifier mNotifier;
private final NetworkPolicyManager mNetworkPolicy;
+ private final StorageManager mStorage;
private final DownloadJobService mJobService;
private final JobParameters mParams;
@@ -245,6 +247,7 @@ public class DownloadThread extends Thread {
mSystemFacade = Helpers.getSystemFacade(mContext);
mNotifier = Helpers.getDownloadNotifier(mContext);
mNetworkPolicy = mContext.getSystemService(NetworkPolicyManager.class);
+ mStorage = mContext.getSystemService(StorageManager.class);
mJobService = service;
mParams = params;
@@ -564,35 +567,23 @@ public class DownloadThread extends Thread {
out = new ParcelFileDescriptor.AutoCloseOutputStream(outPfd);
}
- // Pre-flight disk space requirements, when known
- if (mInfoDelta.mTotalBytes > 0) {
- final long curSize = Os.fstat(outFd).st_size;
- final long newBytes = mInfoDelta.mTotalBytes - curSize;
-
- StorageUtils.ensureAvailableSpace(mContext, outFd, newBytes);
-
- try {
- // We found enough space, so claim it for ourselves
- Os.posix_fallocate(outFd, 0, mInfoDelta.mTotalBytes);
- } catch (ErrnoException e) {
- if (e.errno == OsConstants.ENOSYS || e.errno == OsConstants.ENOTSUP) {
- Log.w(TAG, "fallocate() not supported; falling back to ftruncate()");
- Os.ftruncate(outFd, mInfoDelta.mTotalBytes);
- } else {
- throw e;
- }
- }
- }
-
// Move into place to begin writing
Os.lseek(outFd, mInfoDelta.mCurrentBytes, OsConstants.SEEK_SET);
-
} catch (ErrnoException e) {
throw new StopRequestException(STATUS_FILE_ERROR, e);
} catch (IOException e) {
throw new StopRequestException(STATUS_FILE_ERROR, e);
}
+ try {
+ // Pre-flight disk space requirements, when known
+ if (mInfoDelta.mTotalBytes > 0 && mStorage.isAllocationSupported(outFd)) {
+ mStorage.allocateBytes(outFd, mInfoDelta.mTotalBytes);
+ }
+ } catch (IOException e) {
+ throw new StopRequestException(STATUS_INSUFFICIENT_SPACE_ERROR, e);
+ }
+
// Start streaming data, periodically watch for pause/cancel
// commands and checking disk space as needed.
transferData(in, out, outFd);
@@ -650,14 +641,6 @@ public class DownloadThread extends Thread {
}
try {
- // When streaming, ensure space before each write
- if (mInfoDelta.mTotalBytes == -1) {
- final long curSize = Os.fstat(outFd).st_size;
- final long newBytes = (mInfoDelta.mCurrentBytes + len) - curSize;
-
- StorageUtils.ensureAvailableSpace(mContext, outFd, newBytes);
- }
-
out.write(buffer, 0, len);
mMadeProgress = true;
@@ -665,8 +648,6 @@ public class DownloadThread extends Thread {
updateProgress(outFd);
- } catch (ErrnoException e) {
- throw new StopRequestException(STATUS_FILE_ERROR, e);
} catch (IOException e) {
throw new StopRequestException(STATUS_FILE_ERROR, e);
}
@@ -674,7 +655,8 @@ public class DownloadThread extends Thread {
// Finished without error; verify length if known
if (mInfoDelta.mTotalBytes != -1 && mInfoDelta.mCurrentBytes != mInfoDelta.mTotalBytes) {
- throw new StopRequestException(STATUS_HTTP_DATA_ERROR, "Content length mismatch");
+ throw new StopRequestException(STATUS_HTTP_DATA_ERROR, "Content length mismatch; found "
+ + mInfoDelta.mCurrentBytes + " instead of " + mInfoDelta.mTotalBytes);
}
}
@@ -743,7 +725,8 @@ public class DownloadThread extends Thread {
if (info.isRoaming() && !mInfo.isRoamingAllowed()) {
throw new StopRequestException(STATUS_WAITING_FOR_NETWORK, "Network is roaming");
}
- if (info.isMetered() && !mInfo.isMeteredAllowed(mInfoDelta.mTotalBytes)) {
+ if (mSystemFacade.isNetworkMetered(mNetwork)
+ && !mInfo.isMeteredAllowed(mInfoDelta.mTotalBytes)) {
throw new StopRequestException(STATUS_WAITING_FOR_NETWORK, "Network is metered");
}
}
diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java
index 7354076b..2b55eb87 100644
--- a/src/com/android/providers/downloads/Helpers.java
+++ b/src/com/android/providers/downloads/Helpers.java
@@ -556,14 +556,6 @@ public class Helpers {
return context.getCacheDir();
}
- case Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION:
- if (running) {
- return new File(Environment.getDownloadCacheDirectory(),
- Constants.DIRECTORY_CACHE_RUNNING);
- } else {
- return Environment.getDownloadCacheDirectory();
- }
-
case Downloads.Impl.DESTINATION_EXTERNAL:
final File target = new File(
Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS);
diff --git a/src/com/android/providers/downloads/RealSystemFacade.java b/src/com/android/providers/downloads/RealSystemFacade.java
index df1d245f..1c2ba581 100644
--- a/src/com/android/providers/downloads/RealSystemFacade.java
+++ b/src/com/android/providers/downloads/RealSystemFacade.java
@@ -25,6 +25,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.security.NetworkSecurityPolicy;
import android.security.net.config.ApplicationConfig;
@@ -61,6 +62,12 @@ class RealSystemFacade implements SystemFacade {
}
@Override
+ public boolean isNetworkMetered(Network network) {
+ return !mContext.getSystemService(ConnectivityManager.class).getNetworkCapabilities(network)
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ }
+
+ @Override
public long getMaxBytesOverMobile() {
final Long value = DownloadManager.getMaxBytesOverMobile(mContext);
return (value == null) ? Long.MAX_VALUE : value;
diff --git a/src/com/android/providers/downloads/StorageUtils.java b/src/com/android/providers/downloads/StorageUtils.java
index d7a5c33b..4d332816 100644
--- a/src/com/android/providers/downloads/StorageUtils.java
+++ b/src/com/android/providers/downloads/StorageUtils.java
@@ -16,185 +16,24 @@
package com.android.providers.downloads;
-import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.provider.Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR;
-import static android.text.format.DateUtils.DAY_IN_MILLIS;
-import static com.android.providers.downloads.Constants.TAG;
-
import android.app.DownloadManager;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.pm.IPackageDataObserver;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.os.Environment;
-import android.provider.Downloads;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
-import android.system.StructStatVfs;
-import android.text.TextUtils;
-import android.util.Slog;
-import com.android.internal.annotations.VisibleForTesting;
import com.google.android.collect.Lists;
-import com.google.android.collect.Sets;
-
-import libcore.io.IoUtils;
import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
/**
* Utility methods for managing storage space related to
* {@link DownloadManager}.
*/
public class StorageUtils {
-
- /**
- * Minimum age for a file to be considered for deletion.
- */
- static final long MIN_DELETE_AGE = DAY_IN_MILLIS;
-
- /**
- * Reserved disk space to avoid filling disk.
- */
- static final long RESERVED_BYTES = 32 * MB_IN_BYTES;
-
- @VisibleForTesting
- static boolean sForceFullEviction = false;
-
- /**
- * Ensure that requested free space exists on the partition backing the
- * given {@link FileDescriptor}. If not enough space is available, it tries
- * freeing up space as follows:
- * <ul>
- * <li>If backed by the data partition (including emulated external
- * storage), then ask {@link PackageManager} to free space from cache
- * directories.
- * <li>If backed by the cache partition, then try deleting older downloads
- * to free space.
- * </ul>
- */
- public static void ensureAvailableSpace(Context context, FileDescriptor fd, long bytes)
- throws IOException, StopRequestException {
-
- long availBytes = getAvailableBytes(fd);
- if (availBytes >= bytes) {
- // Underlying partition has enough space; go ahead
- return;
- }
-
- // Not enough space, let's try freeing some up. Start by tracking down
- // the backing partition.
- final long dev;
- try {
- dev = Os.fstat(fd).st_dev;
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
- }
-
- // TODO: teach about evicting caches on adopted secondary storage devices
- final long dataDev = getDeviceId(Environment.getDataDirectory());
- final long cacheDev = getDeviceId(Environment.getDownloadCacheDirectory());
- final long externalDev = getDeviceId(Environment.getExternalStorageDirectory());
-
- if (dev == dataDev || (dev == externalDev && Environment.isExternalStorageEmulated())) {
- // File lives on internal storage; ask PackageManager to try freeing
- // up space from cache directories.
- final PackageManager pm = context.getPackageManager();
- final ObserverLatch observer = new ObserverLatch();
- pm.freeStorageAndNotify(sForceFullEviction ? Long.MAX_VALUE : bytes, observer);
-
- try {
- if (!observer.latch.await(30, TimeUnit.SECONDS)) {
- throw new IOException("Timeout while freeing disk space");
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
-
- } else if (dev == cacheDev) {
- // Try removing old files on cache partition
- freeCacheStorage(bytes);
- }
-
- // Did we free enough space?
- availBytes = getAvailableBytes(fd);
- if (availBytes < bytes) {
- throw new StopRequestException(STATUS_INSUFFICIENT_SPACE_ERROR,
- "Not enough free space; " + bytes + " requested, " + availBytes + " available");
- }
- }
-
- /**
- * Free requested space on cache partition, deleting oldest files first.
- * We're only focused on freeing up disk space, and rely on the next orphan
- * pass to clean up database entries.
- */
- private static void freeCacheStorage(long bytes) {
- // Only consider finished downloads
- final List<ConcreteFile> files = listFilesRecursive(
- Environment.getDownloadCacheDirectory(), Constants.DIRECTORY_CACHE_RUNNING,
- android.os.Process.myUid());
-
- Slog.d(TAG, "Found " + files.size() + " downloads on cache");
-
- Collections.sort(files, new Comparator<ConcreteFile>() {
- @Override
- public int compare(ConcreteFile lhs, ConcreteFile rhs) {
- return Long.compare(lhs.file.lastModified(), rhs.file.lastModified());
- }
- });
-
- final long now = System.currentTimeMillis();
- for (ConcreteFile file : files) {
- if (bytes <= 0) break;
-
- if (now - file.file.lastModified() < MIN_DELETE_AGE) {
- Slog.d(TAG, "Skipping recently modified " + file.file);
- } else {
- final long len = file.file.length();
- Slog.d(TAG, "Deleting " + file.file + " to reclaim " + len);
- bytes -= len;
- file.file.delete();
- }
- }
- }
-
- /**
- * Return number of available bytes on the filesystem backing the given
- * {@link FileDescriptor}, minus any {@link #RESERVED_BYTES} buffer.
- */
- private static long getAvailableBytes(FileDescriptor fd) throws IOException {
- try {
- final StructStatVfs stat = Os.fstatvfs(fd);
- return (stat.f_bavail * stat.f_bsize) - RESERVED_BYTES;
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
- }
- }
-
- private static long getDeviceId(File file) {
- try {
- return Os.stat(file.getAbsolutePath()).st_dev;
- } catch (ErrnoException e) {
- // Safe since dev_t is uint
- return -1;
- }
- }
-
/**
* Return list of all normal files under the given directory, traversing
* directories recursively.
@@ -260,13 +99,4 @@ public class StorageUtils {
return false;
}
}
-
- static class ObserverLatch extends IPackageDataObserver.Stub {
- public final CountDownLatch latch = new CountDownLatch(1);
-
- @Override
- public void onRemoveCompleted(String packageName, boolean succeeded) {
- latch.countDown();
- }
- }
}
diff --git a/src/com/android/providers/downloads/SystemFacade.java b/src/com/android/providers/downloads/SystemFacade.java
index c34317cb..53d14041 100644
--- a/src/com/android/providers/downloads/SystemFacade.java
+++ b/src/com/android/providers/downloads/SystemFacade.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import java.security.GeneralSecurityException;
@@ -35,6 +36,8 @@ interface SystemFacade {
public NetworkInfo getNetworkInfo(Network network, int uid, boolean ignoreBlocked);
+ public boolean isNetworkMetered(Network network);
+
/**
* @return maximum size, in bytes, of downloads that may go over a mobile connection; or null if
* there's no limit
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index ec73ca2e..0f4166f5 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -21,7 +21,7 @@
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
- <application>
+ <application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
index 813252a8..13d2c36e 100644
--- a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
+++ b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
@@ -171,8 +171,8 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
Helpers.setSystemFacade(mSystemFacade);
mSystemFacade.setUp();
- assertTrue(isDatabaseEmpty()); // ensure we're not messing with real data
- assertTrue(isDatabaseSecureAgainstBadSelection());
+ assertDatabaseEmpty(); // ensure we're not messing with real data
+ assertDatabaseSecureAgainstBadSelection();
mServer = new MockWebServer();
mServer.play();
}
@@ -188,34 +188,23 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
protected void startDownload(long id) {
final JobParameters params = mock(JobParameters.class);
when(params.getJobId()).thenReturn((int) id);
+ getService().onBind(null);
getService().onStartJob(params);
}
- private boolean isDatabaseEmpty() {
- Cursor cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
- null, null, null, null);
- try {
- return cursor.getCount() == 0;
- } finally {
- cursor.close();
+ private void assertDatabaseEmpty() {
+ try (Cursor cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
+ null, null, null, null)) {
+ assertEquals(0, cursor.getCount());
}
}
- private boolean isDatabaseSecureAgainstBadSelection() {
- Cursor cursor = null;
- try {
- cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, null,
- "('1'='1'))) ORDER BY lastmod DESC--", null, null);
+ private void assertDatabaseSecureAgainstBadSelection() {
+ try (Cursor cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, null,
+ "('1'='1'))) ORDER BY lastmod DESC--", null, null)) {
+ fail("Database isn't secure!");
+ } catch (Exception expected) {
}
- catch (Exception e) {
- return true;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return false;
}
/**
diff --git a/tests/src/com/android/providers/downloads/FakeSystemFacade.java b/tests/src/com/android/providers/downloads/FakeSystemFacade.java
index b6b800a2..de483c7f 100644
--- a/tests/src/com/android/providers/downloads/FakeSystemFacade.java
+++ b/tests/src/com/android/providers/downloads/FakeSystemFacade.java
@@ -1,6 +1,6 @@
package com.android.providers.downloads;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -21,6 +21,7 @@ import java.net.URLConnection;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
+
import javax.net.ssl.SSLContext;
public class FakeSystemFacade implements SystemFacade {
@@ -91,12 +92,16 @@ public class FakeSystemFacade implements SystemFacade {
final NetworkInfo info = new NetworkInfo(mActiveNetworkType, 0, null, null);
info.setDetailedState(DetailedState.CONNECTED, null, null);
info.setRoaming(mIsRoaming);
- info.setMetered(mIsMetered);
return info;
}
}
@Override
+ public boolean isNetworkMetered(Network network) {
+ return mIsMetered;
+ }
+
+ @Override
public long getMaxBytesOverMobile() {
return mMaxBytesOverMobile;
}
diff --git a/tests/src/com/android/providers/downloads/StorageTest.java b/tests/src/com/android/providers/downloads/StorageTest.java
deleted file mode 100644
index 95bd3676..00000000
--- a/tests/src/com/android/providers/downloads/StorageTest.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.downloads;
-
-import static android.app.DownloadManager.COLUMN_REASON;
-import static android.app.DownloadManager.ERROR_INSUFFICIENT_SPACE;
-import static android.app.DownloadManager.STATUS_FAILED;
-import static android.app.DownloadManager.STATUS_SUCCESSFUL;
-import static android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION;
-import static android.provider.Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION;
-
-import android.app.DownloadManager;
-import android.content.pm.PackageManager;
-import android.os.Environment;
-import android.os.StatFs;
-import android.provider.Downloads.Impl;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.StructStatVfs;
-import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
-import com.android.providers.downloads.StorageUtils.ObserverLatch;
-import com.google.mockwebserver.MockResponse;
-import com.google.mockwebserver.SocketPolicy;
-
-import libcore.io.ForwardingOs;
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-@MediumTest
-public class StorageTest extends AbstractPublicApiTest {
- private static final String TAG = "StorageTest";
-
- private static final int DOWNLOAD_SIZE = 512 * 1024;
- private static final byte[] DOWNLOAD_BODY;
-
- static {
- DOWNLOAD_BODY = new byte[DOWNLOAD_SIZE];
- for (int i = 0; i < DOWNLOAD_SIZE; i++) {
- DOWNLOAD_BODY[i] = (byte) (i % 32);
- }
- }
-
- private libcore.io.Os mOriginal;
- private long mStealBytes;
-
- public StorageTest() {
- super(new FakeSystemFacade());
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- StorageUtils.sForceFullEviction = true;
- mStealBytes = 0;
-
- mOriginal = libcore.io.Libcore.os;
- libcore.io.Libcore.os = new ForwardingOs(mOriginal) {
- @Override
- public StructStatVfs statvfs(String path) throws ErrnoException {
- return stealBytes(os.statvfs(path));
- }
-
- @Override
- public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException {
- return stealBytes(os.fstatvfs(fd));
- }
-
- private StructStatVfs stealBytes(StructStatVfs s) {
- final long stealBlocks = (mStealBytes + (s.f_bsize - 1)) / s.f_bsize;
- final long f_bavail = s.f_bavail - stealBlocks;
- return new StructStatVfs(s.f_bsize, s.f_frsize, s.f_blocks, s.f_bfree, f_bavail,
- s.f_files, s.f_ffree, s.f_favail, s.f_fsid, s.f_flag, s.f_namemax);
- }
- };
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
- StorageUtils.sForceFullEviction = false;
- mStealBytes = 0;
-
- if (mOriginal != null) {
- libcore.io.Libcore.os = mOriginal;
- }
- }
-
- private enum CacheStatus { CLEAN, DIRTY }
- private enum BodyType { COMPLETE, CHUNKED }
-
- public void testDataDirtyComplete() throws Exception {
- prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
- CacheStatus.DIRTY, BodyType.COMPLETE,
- STATUS_SUCCESSFUL, -1);
- }
-
- public void testDataDirtyChunked() throws Exception {
- prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
- CacheStatus.DIRTY, BodyType.CHUNKED,
- STATUS_SUCCESSFUL, -1);
- }
-
- public void testDataCleanComplete() throws Exception {
- prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
- CacheStatus.CLEAN, BodyType.COMPLETE,
- STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
- }
-
- public void testDataCleanChunked() throws Exception {
- prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
- CacheStatus.CLEAN, BodyType.CHUNKED,
- STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
- }
-
- public void testCacheDirtyComplete() throws Exception {
- prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
- CacheStatus.DIRTY, BodyType.COMPLETE,
- STATUS_SUCCESSFUL, -1);
- }
-
- public void testCacheDirtyChunked() throws Exception {
- prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
- CacheStatus.DIRTY, BodyType.CHUNKED,
- STATUS_SUCCESSFUL, -1);
- }
-
- public void testCacheCleanComplete() throws Exception {
- prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
- CacheStatus.CLEAN, BodyType.COMPLETE,
- STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
- }
-
- public void testCacheCleanChunked() throws Exception {
- prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
- CacheStatus.CLEAN, BodyType.CHUNKED,
- STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
- }
-
- private void prepareAndRunDownload(
- int dest, CacheStatus cache, BodyType body, int expectedStatus, int expectedReason)
- throws Exception {
-
- // Ensure that we've purged everything possible for destination
- final File dirtyDir;
- if (dest == DESTINATION_CACHE_PARTITION) {
- final PackageManager pm = getContext().getPackageManager();
- final ObserverLatch observer = new ObserverLatch();
- pm.freeStorageAndNotify(Long.MAX_VALUE, observer);
-
- try {
- if (!observer.latch.await(30, TimeUnit.SECONDS)) {
- throw new IOException("Timeout while freeing disk space");
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
-
- dirtyDir = getContext().getCacheDir();
-
- } else if (dest == DESTINATION_SYSTEMCACHE_PARTITION) {
- IoUtils.deleteContents(Environment.getDownloadCacheDirectory());
- dirtyDir = Environment.getDownloadCacheDirectory();
-
- } else {
- throw new IllegalArgumentException("Unknown destination");
- }
-
- // Allocate a cache file, if requested, making it large enough and old
- // enough to clear.
- final File dirtyFile;
- if (cache == CacheStatus.DIRTY) {
- dirtyFile = new File(dirtyDir, "cache_file.bin");
- assertTrue(dirtyFile.createNewFile());
- final FileOutputStream os = new FileOutputStream(dirtyFile);
- final int dirtySize = (DOWNLOAD_SIZE * 3) / 2;
- Os.posix_fallocate(os.getFD(), 0, dirtySize);
- IoUtils.closeQuietly(os);
-
- dirtyFile.setLastModified(
- System.currentTimeMillis() - (StorageUtils.MIN_DELETE_AGE * 2));
- } else {
- dirtyFile = null;
- }
-
- // At this point, hide all other disk space to make the download fail;
- // if we have a dirty cache file it can be cleared to let us proceed.
- final long targetFree = StorageUtils.RESERVED_BYTES + (DOWNLOAD_SIZE / 2);
-
- final StatFs stat = new StatFs(dirtyDir.getAbsolutePath());
- Log.d(TAG, "Available bytes (before steal): " + stat.getAvailableBytes());
- mStealBytes = stat.getAvailableBytes() - targetFree;
-
- stat.restat(dirtyDir.getAbsolutePath());
- Log.d(TAG, "Available bytes (after steal): " + stat.getAvailableBytes());
-
- final MockResponse resp = new MockResponse().setResponseCode(200)
- .setHeader("Content-type", "text/plain")
- .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
- if (body == BodyType.CHUNKED) {
- resp.setChunkedBody(DOWNLOAD_BODY, 1021);
- } else {
- resp.setBody(DOWNLOAD_BODY);
- }
- enqueueResponse(resp);
-
- final DownloadManager.Request req = getRequest();
- if (dest == Impl.DESTINATION_SYSTEMCACHE_PARTITION) {
- req.setDestinationToSystemCache();
- }
- final Download download = enqueueRequest(req);
- download.runUntilStatus(expectedStatus);
-
- if (expectedStatus == STATUS_SUCCESSFUL) {
- MoreAsserts.assertEquals(DOWNLOAD_BODY, download.getRawContents());
- }
-
- if (expectedReason != -1) {
- assertEquals(expectedReason, download.getLongField(COLUMN_REASON));
- }
-
- if (dirtyFile != null) {
- assertFalse(dirtyFile.exists());
- }
- }
-}
diff --git a/ui/res/values-el/strings.xml b/ui/res/values-el/strings.xml
index bf1c04ca..334bb7d3 100644
--- a/ui/res/values-el/strings.xml
+++ b/ui/res/values-el/strings.xml
@@ -44,7 +44,7 @@
<string name="cancel_running_download" msgid="5232704030969221112">"Ακύρωση"</string>
<string name="retry_download" msgid="7617100787922717912">"Επανάληψη"</string>
<string name="start_now_download" msgid="1564642872809509681">"Έναρξη τώρα"</string>
- <string name="deselect_all" msgid="6348198946254776764">"Κατάργηση επιλογής όλων"</string>
+ <string name="deselect_all" msgid="6348198946254776764">"Αποεπιλογή όλων"</string>
<string name="select_all" msgid="634074918366265804">"Επιλογή όλων"</string>
<string name="selected_count" msgid="2101564570019753277">"Επιλέχθηκαν <xliff:g id="NUMBER">%1$d</xliff:g> από <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="download_share_dialog" msgid="3355867339806448955">"Κοινοποίηση μέσω"</string>
diff --git a/ui/res/values-hi/strings.xml b/ui/res/values-hi/strings.xml
index c6f74de2..04a8d000 100644
--- a/ui/res/values-hi/strings.xml
+++ b/ui/res/values-hi/strings.xml
@@ -47,5 +47,5 @@
<string name="deselect_all" msgid="6348198946254776764">"कुछ भी ना चुनें"</string>
<string name="select_all" msgid="634074918366265804">"सभी चुनें"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g> में से <xliff:g id="NUMBER">%1$d</xliff:g> चयनित"</string>
- <string name="download_share_dialog" msgid="3355867339806448955">"इसके द्वारा साझा करें"</string>
+ <string name="download_share_dialog" msgid="3355867339806448955">"इसके ज़रिए शेयर करें"</string>
</resources>
diff --git a/ui/res/values-iw/strings.xml b/ui/res/values-iw/strings.xml
index 390e3bcf..d58ee688 100644
--- a/ui/res/values-iw/strings.xml
+++ b/ui/res/values-iw/strings.xml
@@ -22,7 +22,7 @@
<string name="no_downloads" msgid="1029667411186146836">"אין הורדות."</string>
<string name="missing_title" msgid="830115697868833773">"‏&lt;לא ידוע&gt;"</string>
<string name="button_sort_by_size" msgid="7331549713691146251">"מיין לפי גודל"</string>
- <string name="button_sort_by_date" msgid="8800842892684101528">"מיין לפי תאריך"</string>
+ <string name="button_sort_by_date" msgid="8800842892684101528">"מיון לפי תאריך"</string>
<string name="download_queued" msgid="104973307780629904">"בהמתנה בתור"</string>
<string name="download_running" msgid="4656462962155580641">"מתבצע"</string>
<string name="download_success" msgid="7006048006543495236">"בוצע"</string>
diff --git a/ui/res/values-sv/strings.xml b/ui/res/values-sv/strings.xml
index f53a3ce5..68000874 100644
--- a/ui/res/values-sv/strings.xml
+++ b/ui/res/values-sv/strings.xml
@@ -28,7 +28,7 @@
<string name="download_success" msgid="7006048006543495236">"Slutförd"</string>
<string name="download_error" msgid="8081329546008568251">"Misslyckades"</string>
<string name="dialog_title_not_available" msgid="5746317632356158515">"Det gick inte att ladda ned."</string>
- <string name="dialog_failed_body" msgid="587545111677064427">"Vill du prova att hämta filen vid ett senare tillfälle eller vill du ta bort den från kön?"</string>
+ <string name="dialog_failed_body" msgid="587545111677064427">"Vill du prova att kadda ned filen vid ett senare tillfälle eller vill du ta bort den från kön?"</string>
<string name="dialog_title_queued_body" msgid="6760681913815015219">"Fil i kö"</string>
<string name="dialog_queued_body" msgid="708552801635572720">"Filen står i kö för nedladdning, så den är inte tillgänglig än."</string>
<string name="dialog_file_missing_body" msgid="3223012612774276284">"Det gick inte att hitta den nedladdade filen."</string>