diff options
26 files changed, 324 insertions, 540 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" /> @@ -0,0 +1 @@ +jsharkey@android.com diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index c028bb13..19867c40 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -30,7 +30,7 @@ <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"الاحتفاظ بمساحة في ذاكرة التخزين المؤقت للتنزيل"</string> <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"للسماح للتطبيق بتنزيل الملفات إلى ذاكرة التخزين المؤقت للتنزيل والتي لا يمكن حذفها تلقائيًا عندما تكون إدارة التنزيل في حاجة إلى مساحة أكبر."</string> <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"تنزيل الملفات بدون تنبيه"</string> - <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"للسماح للتطبيق بتنزيل ملفات من خلال إدارة التنزيل بدون عرض أية اشعارات للمستخدم."</string> + <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"للسماح للتطبيق بتنزيل ملفات من خلال إدارة التنزيل بدون عرض أي اشعارات للمستخدم."</string> <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"الدخول إلى جميع تنزيلات النظام"</string> <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"للسماح للتطبيق بعرض وتعديل جميع التنزيلات التي بدأت من خلال أي تطبيق على النظام."</string> <string name="download_unknown_title" msgid="7015124071247271585">"<بلا عنوان>"</string> diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 607aa4d5..46f4e2d5 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -19,18 +19,18 @@ <string name="app_label" msgid="3658948994665187911">"Upravitelj za preuzimanja"</string> <string name="storage_description" msgid="7982444311558023664">"Preuzimanja"</string> <string name="permlab_downloadManager" msgid="7779544811202855500">"Pristupite upravitelju za preuzimanja."</string> - <string name="permdesc_downloadManager" msgid="4237406545998908947">"Dopušta aplikacijama da pristupaju upravitelju za preuzimanja i koriste ga za preuzimanje datoteka. Zlonamjerne aplikacije to mogu koristiti da poremete preuzimanja i pristupaju privatnim informacijama."</string> + <string name="permdesc_downloadManager" msgid="4237406545998908947">"Dopušta aplikacijama da pristupaju upravitelju za preuzimanja i koriste ga za preuzimanje fajlova. Zlonamjerne aplikacije to mogu koristiti da poremete preuzimanja i pristupaju privatnim informacijama."</string> <string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Napredne funkcije upravitelja za preuzimanja."</string> <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Dopušta aplikaciji da pristupa naprednim funkcijama upravitelja za preuzimanja. Zlonamjerne aplikacije to mogu koristiti da poremete preuzimanja i pristupaju privatnim informacijama."</string> <string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Šaljite obavještenja o preuzimanju."</string> - <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Dopušta aplikaciji da šalje obavještenja o završenim ažuriranjima. Zlonamjerne aplikacije ovo mogu koristiti da zbune druge aplikacije koje preuzimaju datoteke."</string> + <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Dopušta aplikaciji da šalje obavještenja o završenim ažuriranjima. Zlonamjerne aplikacije ovo mogu koristiti da zbune druge aplikacije koje preuzimaju fajlove."</string> <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Prikaži sva preuzimanja na USB pohranu"</string> <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Prikaži sva preuzimanja na SD karticu"</string> <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Dopušta aplikaciji da vidi sva preuzimanja na SD kartici, bez obzira na to koja ih je aplikacija izvršila."</string> <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Rezerviraj mjesto u keš memoriji preuzimanja"</string> - <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Dopušta aplikaciji da preuzme datoteke u keš memoriju preuzimanja, koja se ne može automatski izbrisati kada upravitelju za preuzimanja treba više prostora."</string> - <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"preuzimanje datoteka bez obavještenja"</string> - <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Dopušta aplikaciji da preuzima datoteke preko upravitelja za preuzimanja bez prikazivanja obavještenja korisniku."</string> + <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Dopušta aplikaciji da preuzme fajlove u keš memoriju preuzimanja, koja se ne može automatski izbrisati kada upravitelju za preuzimanja treba više prostora."</string> + <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"preuzimanje fajlova bez obavještenja"</string> + <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Dopušta aplikaciji da preuzima fajlove preko upravitelja za preuzimanja bez prikazivanja obavještenja korisniku."</string> <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Pristup svim preuzimanjima sistema"</string> <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Dopušta aplikaciji da prikaže i modificira sva preuzimanja koja pokrene bilo koja aplikacija u sistemu."</string> <string name="download_unknown_title" msgid="7015124071247271585">"<Bez naslova>"</string> @@ -46,16 +46,16 @@ <string name="button_cancel_download" msgid="2430166148737975604">"Otkaži"</string> <string name="button_start_now" msgid="792123674007840864">"Pokreni sada"</string> <plurals name="notif_summary_active" formatted="false" msgid="7290448463204837173"> - <item quantity="one">Preuzima se <xliff:g id="NUMBER">%d</xliff:g> datoteka</item> - <item quantity="few">Preuzimaju se <xliff:g id="NUMBER">%d</xliff:g> datoteke</item> - <item quantity="other">Preuzima se <xliff:g id="NUMBER">%d</xliff:g> datoteka</item> + <item quantity="one">Preuzima se <xliff:g id="NUMBER">%d</xliff:g> fajl</item> + <item quantity="few">Preuzimaju se <xliff:g id="NUMBER">%d</xliff:g> fajla</item> + <item quantity="other">Preuzima se <xliff:g id="NUMBER">%d</xliff:g> fajlova</item> </plurals> <plurals name="notif_summary_waiting" formatted="false" msgid="2814217662029273005"> - <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> datoteka na čekanju</item> - <item quantity="few"><xliff:g id="NUMBER">%d</xliff:g> datoteke na čekanju</item> - <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> datoteka na čekanju</item> + <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> fajl na čekanju</item> + <item quantity="few"><xliff:g id="NUMBER">%d</xliff:g> fajla na čekanju</item> + <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> fajlova na čekanju</item> </plurals> - <string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> lijevo"</string> + <string name="download_remaining" msgid="3139295890887972718">"Još <xliff:g id="DURATION">%s</xliff:g>"</string> <string name="download_no_application_title" msgid="7935659741162801699">"Nije moguće otvoriti datoteku"</string> <string name="root_downloads" msgid="4098414876292351487">"Preuzimanja"</string> <string name="download_queued" msgid="3302638231377947451">"U redoslijedu"</string> diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml new file mode 100644 index 00000000..415a164f --- /dev/null +++ b/res/values-en-rCA/strings.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2007 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3658948994665187911">"Download Manager"</string> + <string name="storage_description" msgid="7982444311558023664">"Downloads"</string> + <string name="permlab_downloadManager" msgid="7779544811202855500">"Access download manager."</string> + <string name="permdesc_downloadManager" msgid="4237406545998908947">"Allows the app to access the download manager and to use it to download files. Malicious apps can use this to disrupt downloads and access private information."</string> + <string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Advanced download manager functions."</string> + <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Allows the app to access the download manager\'s advanced functions. Malicious apps can use this to disrupt downloads and access private information."</string> + <string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Send download notifications."</string> + <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Allows the app to send notifications about completed downloads. Malicious apps can use this to confuse other apps that download files."</string> + <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"See all downloads to USB storage"</string> + <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"See all downloads to SD card"</string> + <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Allows the app to see all downloads to the SD card, regardless of which app downloaded them."</string> + <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Reserve space in the download cache"</string> + <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Allows the app to download files to the download cache, which can\'t be deleted automatically when the download manager needs more space."</string> + <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"download files without notification"</string> + <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Allows the application to download files through the download manager without any notification being shown to the user."</string> + <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Access all system downloads"</string> + <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Allows the app to view and modify all downloads initiated by any app on the system."</string> + <string name="download_unknown_title" msgid="7015124071247271585">"<Untitled>"</string> + <string name="notification_download_complete" msgid="5443563299253103667">"Download complete."</string> + <string name="notification_download_failed" msgid="8612136111952014978">"Download unsuccessful."</string> + <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"Download size requires Wi-Fi"</string> + <string name="notification_paused_in_background" msgid="4328508073283591772">"Paused in background"</string> + <string name="wifi_required_title" msgid="1995971416871498179">"Download too large for operator network"</string> + <string name="wifi_required_body" msgid="3067694630143784449">"You must use Wi-Fi to complete this <xliff:g id="SIZE">%s </xliff:g> download. \n\nTouch <xliff:g id="QUEUE_TEXT">%s </xliff:g> to start this download the next time that you\'re connected to a Wi-Fi network."</string> + <string name="wifi_recommended_title" msgid="7441589306734687400">"Queue for download later?"</string> + <string name="wifi_recommended_body" msgid="1314735166699936073">"Starting this <xliff:g id="SIZE">%s </xliff:g> download now may shorten your battery life and/or result in excessive usage of your mobile data connection, which can lead to charges by your mobile operator depending on your data plan.\n\n Touch <xliff:g id="QUEUE_TEXT">%s</xliff:g> to start this download the next time that you\'re connected to a Wi-Fi network."</string> + <string name="button_queue_for_wifi" msgid="422576726189179221">"Queue"</string> + <string name="button_cancel_download" msgid="2430166148737975604">"Cancel"</string> + <string name="button_start_now" msgid="792123674007840864">"Start now"</string> + <plurals name="notif_summary_active" formatted="false" msgid="7290448463204837173"> + <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> files downloading</item> + <item quantity="one">1 file downloading</item> + </plurals> + <plurals name="notif_summary_waiting" formatted="false" msgid="2814217662029273005"> + <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> files waiting</item> + <item quantity="one">1 file waiting</item> + </plurals> + <string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> left"</string> + <string name="download_no_application_title" msgid="7935659741162801699">"Can\'t open file"</string> + <string name="root_downloads" msgid="4098414876292351487">"Downloads"</string> + <string name="download_queued" msgid="3302638231377947451">"Queued"</string> + <string name="download_running" msgid="3925050393361158266">"In progress"</string> + <string name="download_error" msgid="5144180777324573236">"Unsuccessful"</string> + <string name="download_running_percent" msgid="4305080769167320204">"In progress, <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> +</resources> diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml new file mode 100644 index 00000000..e8e9c139 --- /dev/null +++ b/res/values-en-rXC/strings.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2007 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3658948994665187911">"Download Manager"</string> + <string name="storage_description" msgid="7982444311558023664">"Downloads"</string> + <string name="permlab_downloadManager" msgid="7779544811202855500">"Access download manager."</string> + <string name="permdesc_downloadManager" msgid="4237406545998908947">"Allows the app to access the download manager and to use it to download files. Malicious apps can use this to disrupt downloads and access private information."</string> + <string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Advanced download manager functions."</string> + <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Allows the app to access the download manager\'s advanced functions. Malicious apps can use this to disrupt downloads and access private information."</string> + <string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Send download notifications."</string> + <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Allows the app to send notifications about completed downloads. Malicious apps can use this to confuse other apps that download files."</string> + <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"See all downloads to USB storage"</string> + <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"See all downloads to SD card"</string> + <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Allows the app to see all downloads to the SD card, regardless of which app downloaded them."</string> + <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Reserve space in the download cache"</string> + <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Allows the app to download files to the download cache, which can\'t be automatically deleted when the download manager needs more space."</string> + <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"download files without notification"</string> + <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Allows the app to download files through the download manager without any notification being shown to the user."</string> + <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Access all system downloads"</string> + <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Allows the app to view and modify all downloads initiated by any app on the system."</string> + <string name="download_unknown_title" msgid="7015124071247271585">"<Untitled>"</string> + <string name="notification_download_complete" msgid="5443563299253103667">"Download complete."</string> + <string name="notification_download_failed" msgid="8612136111952014978">"Download unsuccessful."</string> + <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"Download size requires Wi-Fi."</string> + <string name="notification_paused_in_background" msgid="4328508073283591772">"Paused in background."</string> + <string name="wifi_required_title" msgid="1995971416871498179">"Download too large for operator network"</string> + <string name="wifi_required_body" msgid="3067694630143784449">"You must use Wi-Fi to complete this <xliff:g id="SIZE">%s </xliff:g> download. \n\nTouch <xliff:g id="QUEUE_TEXT">%s </xliff:g> to start this download the next time you\'re connected to a Wi-Fi network."</string> + <string name="wifi_recommended_title" msgid="7441589306734687400">"Queue for download later?"</string> + <string name="wifi_recommended_body" msgid="1314735166699936073">"Starting this <xliff:g id="SIZE">%s </xliff:g> download now may shorten your battery life and/or result in excessive usage of your mobile data connection, which can lead to charges by your mobile operator depending on your data plan.\n\n Touch <xliff:g id="QUEUE_TEXT">%s</xliff:g> to start this download the next time you\'re connected to a Wi-Fi network."</string> + <string name="button_queue_for_wifi" msgid="422576726189179221">"Queue"</string> + <string name="button_cancel_download" msgid="2430166148737975604">"Cancel"</string> + <string name="button_start_now" msgid="792123674007840864">"Start now"</string> + <plurals name="notif_summary_active" formatted="false" msgid="7290448463204837173"> + <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> files downloading</item> + <item quantity="one">1 file downloading</item> + </plurals> + <plurals name="notif_summary_waiting" formatted="false" msgid="2814217662029273005"> + <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> files waiting</item> + <item quantity="one">1 file waiting</item> + </plurals> + <string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> left"</string> + <string name="download_no_application_title" msgid="7935659741162801699">"Can\'t open file"</string> + <string name="root_downloads" msgid="4098414876292351487">"Downloads"</string> + <string name="download_queued" msgid="3302638231377947451">"Queued"</string> + <string name="download_running" msgid="3925050393361158266">"In progress"</string> + <string name="download_error" msgid="5144180777324573236">"Unsuccessful"</string> + <string name="download_running_percent" msgid="4305080769167320204">"In progress, <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> +</resources> diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 83489be8..ed341cc5 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -16,7 +16,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_label" msgid="3658948994665187911">"Gestionnaire de téléchargement"</string> + <string name="app_label" msgid="3658948994665187911">"Gest. de tél."</string> <string name="storage_description" msgid="7982444311558023664">"Téléchargements"</string> <string name="permlab_downloadManager" msgid="7779544811202855500">"Accéder au gestionnaire de téléchargement."</string> <string name="permdesc_downloadManager" msgid="4237406545998908947">"Permet à l\'application d\'accéder au gestionnaire de téléchargement et de l\'utiliser pour télécharger des fichiers. Les applications malveillantes peuvent utiliser cette fonctionnalité pour perturber les téléchargements et accéder à des informations confidentielles."</string> diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 4eec41bc..bccb91ad 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -22,15 +22,15 @@ <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> <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"שמור מקום במטמון ההורדות"</string> <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"מאפשר לאפליקציה להוריד קבצים למטמון ההורדות שלא ניתן למחוק באופן אוטומטי כאשר דרוש למנהל ההורדות שטח נוסף."</string> <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"הורד קבצים ללא התראה"</string> - <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"מאפשר לאפליקציה להוריד קבצים דרך מנהל ההורדות מבלי להציג התראות כלשהן בפני המשתמש."</string> + <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"מאפשר לאפליקציה להוריד קבצים דרך מנהל ההורדות מבלי להציג הודעות כלשהן בפני המשתמש."</string> <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"גישה לכל הורדות המערכת"</string> <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"מאפשר לאפליקציה להציג ולשנות את כל ההורדות שהופעלו על ידי אפליקציה כלשהי במערכת."</string> <string name="download_unknown_title" msgid="7015124071247271585">"<ללא כותרת>"</string> diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 11659920..f7917354 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -27,7 +27,7 @@ <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Angalia vipakuliwa vyote vilivyowekwa kwenye hifadhi ya USB"</string> <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Tazama vipakuliwa vyote vilivyoenda kwenye kadi ya SD"</string> <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Huruhusu programu kuona vipakuliwa vyote kwenye kadi ya SD, pasi kuzingatia ni programu gani iliyozipakua."</string> - <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Hifadhi nafasi katika akiba ya upakuaji"</string> + <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Weka nafasi katika akiba ya upakuaji"</string> <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Huruhusu programu kupakua faili hadi akiba ya vipakuliwa ambayo haiwezi kufutwa kiotomatiki wakati kidhibiti upakuaji kinahitaji nafasi zaidi."</string> <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"kupakua faili bila kutoa arifa"</string> <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Huruhusu programu kupakua faili kupitia kidhibiti vipakuliwa bila mtumiaji kuonyeshwa arifa yoyote."</string> diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index d9bf1746..d1d341e5 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -27,8 +27,8 @@ <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Xem t.cả t.xuống trong b.nhớ USB"</string> <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Xem tất cả nội dung tải xuống về thẻ SD"</string> <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Cho phép ứng dụng xem tất cả các bản tải xuống thẻ SD, bất kể ứng dụng đã tải xuống chúng là ứng dụng gì."</string> - <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Giữ lại dung lượng trong bộ nhớ cache tải xuống"</string> - <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Cho phép ứng dụng tải tệp xuống bộ nhớ cache, không thể tự động xóa bộ nhớ này khi trình quản lý tải xuống cần thêm dung lượng."</string> + <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Giữ lại dung lượng trong bộ nhớ đệm tải xuống"</string> + <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Cho phép ứng dụng tải tệp xuống bộ nhớ đệm, không thể tự động xóa bộ nhớ này khi trình quản lý tải xuống cần thêm dung lượng."</string> <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"tải xuống tệp không có thông báo"</string> <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Cho phép ứng dụng tải xuống tệp qua trình quản lý tải xuống mà không hiển thị thông báo cho người dùng."</string> <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Truy cập tất cả các bản tải xuống trong hệ thống"</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 210cabff..ceb17da6 100644 --- a/src/com/android/providers/downloads/DownloadProvider.java +++ b/src/com/android/providers/downloads/DownloadProvider.java @@ -87,6 +87,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"; @@ -194,6 +196,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); } /** @@ -538,8 +541,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"); } @@ -566,12 +568,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); } @@ -1278,6 +1274,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..d3ec568c 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.isActiveNetworkMeteredForUid(mInfo.mUid) + && !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..2d9b3a30 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,18 @@ 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 boolean isActiveNetworkMeteredForUid(int uid) { + return mContext.getSystemService(ConnectivityManager.class) + .isActiveNetworkMeteredForUid(uid); + } + + @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..dec0cb28 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,10 @@ interface SystemFacade { public NetworkInfo getNetworkInfo(Network network, int uid, boolean ignoreBlocked); + public boolean isNetworkMetered(Network network); + + public boolean isActiveNetworkMeteredForUid(int uid); + /** * @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..aa7b8af4 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,21 @@ 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 boolean isActiveNetworkMeteredForUid(int uid) { + 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-bs/strings.xml b/ui/res/values-bs/strings.xml index 12c9a46f..c86fdf72 100644 --- a/ui/res/values-bs/strings.xml +++ b/ui/res/values-bs/strings.xml @@ -28,14 +28,14 @@ <string name="download_success" msgid="7006048006543495236">"Završeno"</string> <string name="download_error" msgid="8081329546008568251">"Neuspješno"</string> <string name="dialog_title_not_available" msgid="5746317632356158515">"Nije bilo moguće preuzeti"</string> - <string name="dialog_failed_body" msgid="587545111677064427">"Da li želite ponovo pokušati s preuzimanjem datoteke kasnije ili je želite izbrisati s redoslijeda?"</string> - <string name="dialog_title_queued_body" msgid="6760681913815015219">"Datoteka je u redoslijedu"</string> - <string name="dialog_queued_body" msgid="708552801635572720">"Datoteka je na redoslijedu za kasnije preuzimanje tako da još nije dostupna."</string> + <string name="dialog_failed_body" msgid="587545111677064427">"Da li želite ponovo pokušati s preuzimanjem fajla kasnije ili je želite izbrisati s redoslijeda?"</string> + <string name="dialog_title_queued_body" msgid="6760681913815015219">"Fajl je u redu čekanja"</string> + <string name="dialog_queued_body" msgid="708552801635572720">"Fajl je u redu čekanja za kasnije preuzimanje tako da još nije dostupan."</string> <string name="dialog_file_missing_body" msgid="3223012612774276284">"Nije moguće pronaći preuzetu datoteku."</string> <string name="dialog_insufficient_space_on_external" msgid="8692452156251449195">"Nije moguće dovršiti preuzimanje. Nema dovoljno prostora na eksternoj pohrani."</string> <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"Nije moguće dovršiti preuzimanje. Nema dovoljno prostora na internoj pohrani za preuzimanja."</string> <string name="dialog_cannot_resume" msgid="8664509751358983543">"Preuzimanje je prekinuto i ne može se nastaviti."</string> - <string name="dialog_file_already_exists" msgid="8308563940663449590">"Nije moguće preuzeti. Odredišna datoteka već postoji."</string> + <string name="dialog_file_already_exists" msgid="8308563940663449590">"Nije moguće preuzeti. Odredišni fajl već postoji."</string> <string name="dialog_media_not_found" msgid="4468088418758018765">"Nije moguće preuzeti. Vanjski medij nije dostupan."</string> <string name="download_no_application_title" msgid="7024782176657362251">"Nije moguće otvoriti datoteku"</string> <string name="remove_download" msgid="6372920256257247857">"Ukloni"</string> diff --git a/ui/res/values-en-rCA/strings.xml b/ui/res/values-en-rCA/strings.xml new file mode 100644 index 00000000..05289ccd --- /dev/null +++ b/ui/res/values-en-rCA/strings.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2010 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3070921713463294774">"Downloads"</string> + <string name="download_title_sorted_by_date" msgid="5898014492155434221">"Downloads - Sorted by date"</string> + <string name="download_title_sorted_by_size" msgid="1417193166677094813">"Downloads - Sorted by size"</string> + <string name="no_downloads" msgid="1029667411186146836">"No downloads."</string> + <string name="missing_title" msgid="830115697868833773">"<Unknown>"</string> + <string name="button_sort_by_size" msgid="7331549713691146251">"Sort by size"</string> + <string name="button_sort_by_date" msgid="8800842892684101528">"Sort by date"</string> + <string name="download_queued" msgid="104973307780629904">"Queued"</string> + <string name="download_running" msgid="4656462962155580641">"In progress"</string> + <string name="download_success" msgid="7006048006543495236">"Complete"</string> + <string name="download_error" msgid="8081329546008568251">"Unsuccessful"</string> + <string name="dialog_title_not_available" msgid="5746317632356158515">"Couldn\'t download"</string> + <string name="dialog_failed_body" msgid="587545111677064427">"Do you want to retry downloading the file later or delete it from the queue?"</string> + <string name="dialog_title_queued_body" msgid="6760681913815015219">"File in queue"</string> + <string name="dialog_queued_body" msgid="708552801635572720">"This file is queued for future download, so isn\'t available yet."</string> + <string name="dialog_file_missing_body" msgid="3223012612774276284">"Can\'t find the downloaded file."</string> + <string name="dialog_insufficient_space_on_external" msgid="8692452156251449195">"Can\'t finish download. There isn\'t enough space on external storage."</string> + <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"Cannot finish download. There is not enough space on internal download storage."</string> + <string name="dialog_cannot_resume" msgid="8664509751358983543">"Download was interrupted and can\'t be resumed."</string> + <string name="dialog_file_already_exists" msgid="8308563940663449590">"Can\'t download. The destination file already exists."</string> + <string name="dialog_media_not_found" msgid="4468088418758018765">"Cannot download. The external media are not available."</string> + <string name="download_no_application_title" msgid="7024782176657362251">"Can\'t open file"</string> + <string name="remove_download" msgid="6372920256257247857">"Remove"</string> + <string name="delete_download" msgid="76629022653866471">"Delete"</string> + <string name="keep_queued_download" msgid="5144882786014818569">"Keep"</string> + <string name="cancel_running_download" msgid="5232704030969221112">"Cancel"</string> + <string name="retry_download" msgid="7617100787922717912">"Retry"</string> + <string name="start_now_download" msgid="1564642872809509681">"Start now"</string> + <string name="deselect_all" msgid="6348198946254776764">"Deselect all"</string> + <string name="select_all" msgid="634074918366265804">"Select all"</string> + <string name="selected_count" msgid="2101564570019753277">"Selected <xliff:g id="NUMBER">%1$d</xliff:g> out of <xliff:g id="TOTAL">%2$d</xliff:g>"</string> + <string name="download_share_dialog" msgid="3355867339806448955">"Share via"</string> +</resources> diff --git a/ui/res/values-en-rXC/strings.xml b/ui/res/values-en-rXC/strings.xml new file mode 100644 index 00000000..94dc0759 --- /dev/null +++ b/ui/res/values-en-rXC/strings.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2010 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3070921713463294774">"Downloads"</string> + <string name="download_title_sorted_by_date" msgid="5898014492155434221">"Downloads - Sorted by date"</string> + <string name="download_title_sorted_by_size" msgid="1417193166677094813">"Downloads - Sorted by size"</string> + <string name="no_downloads" msgid="1029667411186146836">"No downloads."</string> + <string name="missing_title" msgid="830115697868833773">"<Unknown>"</string> + <string name="button_sort_by_size" msgid="7331549713691146251">"Sort by size"</string> + <string name="button_sort_by_date" msgid="8800842892684101528">"Sort by date"</string> + <string name="download_queued" msgid="104973307780629904">"Queued"</string> + <string name="download_running" msgid="4656462962155580641">"In progress"</string> + <string name="download_success" msgid="7006048006543495236">"Complete"</string> + <string name="download_error" msgid="8081329546008568251">"Unsuccessful"</string> + <string name="dialog_title_not_available" msgid="5746317632356158515">"Couldn\'t download"</string> + <string name="dialog_failed_body" msgid="587545111677064427">"Do you want to retry downloading the file later or delete it from the queue?"</string> + <string name="dialog_title_queued_body" msgid="6760681913815015219">"File in queue"</string> + <string name="dialog_queued_body" msgid="708552801635572720">"This file is queued for future download so isn\'t available yet."</string> + <string name="dialog_file_missing_body" msgid="3223012612774276284">"Can\'t find the downloaded file."</string> + <string name="dialog_insufficient_space_on_external" msgid="8692452156251449195">"Can\'t finish download. There isn\'t enough space on external storage."</string> + <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"Can\'t finish download. There isn\'t enough space on internal download storage."</string> + <string name="dialog_cannot_resume" msgid="8664509751358983543">"Download was interrupted and can\'t be resumed."</string> + <string name="dialog_file_already_exists" msgid="8308563940663449590">"Can\'t download. The destination file already exists."</string> + <string name="dialog_media_not_found" msgid="4468088418758018765">"Can\'t download. The external media isn\'t available."</string> + <string name="download_no_application_title" msgid="7024782176657362251">"Can\'t open file"</string> + <string name="remove_download" msgid="6372920256257247857">"Remove"</string> + <string name="delete_download" msgid="76629022653866471">"Delete"</string> + <string name="keep_queued_download" msgid="5144882786014818569">"Keep"</string> + <string name="cancel_running_download" msgid="5232704030969221112">"Cancel"</string> + <string name="retry_download" msgid="7617100787922717912">"Retry"</string> + <string name="start_now_download" msgid="1564642872809509681">"Start now"</string> + <string name="deselect_all" msgid="6348198946254776764">"Deselect all"</string> + <string name="select_all" msgid="634074918366265804">"Select all"</string> + <string name="selected_count" msgid="2101564570019753277">"Selected <xliff:g id="NUMBER">%1$d</xliff:g> out of <xliff:g id="TOTAL">%2$d</xliff:g>"</string> + <string name="download_share_dialog" msgid="3355867339806448955">"Share via"</string> +</resources> |