summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--res/values-lt/strings.xml10
-rw-r--r--res/values-my-rMM/strings.xml4
-rw-r--r--src/com/android/providers/downloads/DownloadNotifier.java4
-rw-r--r--src/com/android/providers/downloads/DownloadProvider.java67
-rw-r--r--src/com/android/providers/downloads/DownloadStorageProvider.java145
-rw-r--r--src/com/android/providers/downloads/DownloadThread.java1
-rw-r--r--src/com/android/providers/downloads/OpenHelper.java17
-rw-r--r--ui/res/values-my-rMM/strings.xml4
9 files changed, 185 insertions, 69 deletions
diff --git a/Android.mk b/Android.mk
index d69e42f1..75597f59 100644
--- a/Android.mk
+++ b/Android.mk
@@ -11,6 +11,8 @@ LOCAL_PRIVILEGED_MODULE := true
LOCAL_STATIC_JAVA_LIBRARIES := guava \
android-support-documents-archive
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.providers.downloads.*
+
include $(BUILD_PACKAGE)
# build UI + tests
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 856d33d8..ef2d382b 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -18,18 +18,18 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3658948994665187911">"Atsisiuntimų tvarkytuvė"</string>
<string name="permlab_downloadManager" msgid="7779544811202855500">"Pasiekti atsisiuntimo tvarkyklę."</string>
- <string name="permdesc_downloadManager" msgid="4237406545998908947">"Leidžiama programai pasiekti atsisiuntimų tvarkyklę ir naudoti ją failams atsisiųsti. Kenkėjiškos programos gali naudoti šią funkciją atsisiuntimams stabdyti ir asmeninei informacijai pasiekti."</string>
- <string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Išplėstinės atsisiuntimo tvarkyklės funkcijos."</string>
- <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Leidžiama programai pasiekti išplėstines atsisiuntimų tvarkyklės funkcijas. Kenkėjiškos programos gali naudoti šią funkciją atsisiuntimams stabdyti ir asmeninei informacijai gauti."</string>
+ <string name="permdesc_downloadManager" msgid="4237406545998908947">"Leidžiama programai pasiekti atsisiuntimų tvarkytuvę ir naudoti ją failams atsisiųsti. Kenkėjiškos programos gali naudoti šią funkciją atsisiuntimams stabdyti ir asmeninei informacijai pasiekti."</string>
+ <string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Išplėstinės atsisiuntimo tvarkytuvės funkcijos."</string>
+ <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Leidžiama programai pasiekti išplėstines atsisiuntimų tvarkytuvės funkcijas. Kenkėjiškos programos gali naudoti šią funkciją atsisiuntimams stabdyti ir asmeninei informacijai gauti."</string>
<string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Siųsti atsisiuntimo pranešimus."</string>
<string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Leidžiama programai siųsti pranešimus apie baigtus atsisiuntimus. Kenkėjiškos programos gali naudoti šią funkciją, kad supainiotų kitas programas, kurias naudojant siunčiami failai."</string>
<string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Žr. visus atsis. į USB atm."</string>
<string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Žiūrėti visus atsisiuntimus į SD kortelę"</string>
<string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Leidžiama programai matyti visus atsisiuntimus į SD kortelę, neatsižvelgiant į tai, kokia programa jie buvo atsiųsti."</string>
<string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Rezervuoti vietą atsisiuntimo talpykloje"</string>
- <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Leidžiama programai atsisiųsti failus į atsisiuntimo talpyklą, kurios negalima automatiškai ištrinti, kai atsisiuntimo tvarkyklei prireikia daugiau vietos."</string>
+ <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Leidžiama programai atsisiųsti failus į atsisiuntimo talpyklą, kurios negalima automatiškai ištrinti, kai atsisiuntimo tvarkytuvei prireikia daugiau vietos."</string>
<string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"atsisiųsti failus be pranešimo"</string>
- <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Leidžiama programai atsisiųsti failus naudojant atsisiuntimo tvarkyklę, naudotojui nepateikus jokio pranešimo."</string>
+ <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Leidžiama programai atsisiųsti failus naudojant atsisiuntimo tvarkytuvę, naudotojui nepateikus jokio pranešimo."</string>
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Pasiekti visus sistemos atsisiuntimus"</string>
<string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Leidžiama programai žiūrėti ir keisti visus atsisiuntimus, pradėtus naudojant bet kokią sistemos programą."</string>
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;Be pavadinimo&gt;"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 209fca4a..4cabab23 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -22,7 +22,7 @@
<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="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>
@@ -42,7 +42,7 @@
<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="button_queue_for_wifi" msgid="422576726189179221">"တန်းစီရန်"</string>
- <string name="button_cancel_download" msgid="2430166148737975604">"မလုပ်တော့ပါ"</string>
+ <string name="button_cancel_download" msgid="2430166148737975604">"မလုပ်တော့"</string>
<string name="button_start_now" msgid="792123674007840864">"ယခုအချိန်မှစတင်ပါ"</string>
<plurals name="notif_summary_active" formatted="false" msgid="7290448463204837173">
<item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> ဖိုင်များကို ဒေါင်းလုဒ် လုပ်နေပါသည်</item>
diff --git a/src/com/android/providers/downloads/DownloadNotifier.java b/src/com/android/providers/downloads/DownloadNotifier.java
index d5808690..d13bb5e2 100644
--- a/src/com/android/providers/downloads/DownloadNotifier.java
+++ b/src/com/android/providers/downloads/DownloadNotifier.java
@@ -213,7 +213,9 @@ public class DownloadNotifier {
downloadIds);
builder.setContentIntent(PendingIntent.getBroadcast(mContext,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
- builder.setOngoing(true);
+ if (type == TYPE_ACTIVE) {
+ builder.setOngoing(true);
+ }
// Add a Cancel action
final Uri cancelUri = new Uri.Builder().scheme("cancel-dl").appendPath(tag).build();
diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java
index d50eedba..72975582 100644
--- a/src/com/android/providers/downloads/DownloadProvider.java
+++ b/src/com/android/providers/downloads/DownloadProvider.java
@@ -18,7 +18,6 @@ package com.android.providers.downloads;
import static android.provider.BaseColumns._ID;
import static android.provider.Downloads.Impl.COLUMN_DESTINATION;
-import static android.provider.Downloads.Impl.COLUMN_MEDIAPROVIDER_URI;
import static android.provider.Downloads.Impl.COLUMN_MEDIA_SCANNED;
import static android.provider.Downloads.Impl.COLUMN_MIME_TYPE;
import static android.provider.Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD;
@@ -29,6 +28,7 @@ import android.app.DownloadManager;
import android.app.DownloadManager.Request;
import android.app.job.JobScheduler;
import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -1077,10 +1077,14 @@ public final class DownloadProvider extends ContentProvider {
Helpers.validateSelection(where, sAppReadableColumnsSet);
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final Context context = getContext();
+ final ContentResolver resolver = context.getContentResolver();
+
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
boolean updateSchedule = false;
+ boolean isCompleting = false;
ContentValues filteredValues;
if (Binder.getCallingPid() != Process.myPid()) {
@@ -1121,6 +1125,7 @@ public final class DownloadProvider extends ContentProvider {
if (isRestart || isUserBypassingSizeLimit) {
updateSchedule = true;
}
+ isCompleting = status != null && Downloads.Impl.isStatusCompleted(status);
}
int match = sURIMatcher.match(uri);
@@ -1137,14 +1142,20 @@ public final class DownloadProvider extends ContentProvider {
final SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
count = db.update(DB_TABLE, filteredValues, selection.getSelection(),
selection.getParameters());
- if (updateSchedule) {
+ if (updateSchedule || isCompleting) {
final long token = Binder.clearCallingIdentity();
- try {
- try (Cursor cursor = db.query(DB_TABLE, new String[] { _ID },
- selection.getSelection(), selection.getParameters(),
- null, null, null)) {
- while (cursor.moveToNext()) {
- Helpers.scheduleJob(getContext(), cursor.getInt(0));
+ try (Cursor cursor = db.query(DB_TABLE, null, selection.getSelection(),
+ selection.getParameters(), null, null, null)) {
+ final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver,
+ cursor);
+ final DownloadInfo info = new DownloadInfo(context);
+ while (cursor.moveToNext()) {
+ reader.updateFromDatabase(info);
+ if (updateSchedule) {
+ Helpers.scheduleJob(context, info);
+ }
+ if (isCompleting) {
+ info.sendIntentIfRequested();
}
}
} finally {
@@ -1207,7 +1218,10 @@ public final class DownloadProvider extends ContentProvider {
Helpers.validateSelection(where, sAppReadableColumnsSet);
}
- final JobScheduler scheduler = getContext().getSystemService(JobScheduler.class);
+ final Context context = getContext();
+ final ContentResolver resolver = context.getContentResolver();
+ final JobScheduler scheduler = context.getSystemService(JobScheduler.class);
+
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
int match = sURIMatcher.match(uri);
@@ -1219,16 +1233,18 @@ public final class DownloadProvider extends ContentProvider {
final SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
deleteRequestHeaders(db, selection.getSelection(), selection.getParameters());
- try (Cursor cursor = db.query(DB_TABLE, new String[] {
- _ID, _DATA, COLUMN_MEDIAPROVIDER_URI
- }, selection.getSelection(), selection.getParameters(), null, null, null)) {
+ try (Cursor cursor = db.query(DB_TABLE, null, selection.getSelection(),
+ selection.getParameters(), null, null, null)) {
+ final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver, cursor);
+ final DownloadInfo info = new DownloadInfo(context);
while (cursor.moveToNext()) {
- final long id = cursor.getLong(0);
- scheduler.cancel((int) id);
- revokeAllDownloadsPermission(id);
- DownloadStorageProvider.onDownloadProviderDelete(getContext(), id);
+ reader.updateFromDatabase(info);
+ scheduler.cancel((int) info.mId);
+
+ revokeAllDownloadsPermission(info.mId);
+ DownloadStorageProvider.onDownloadProviderDelete(getContext(), info.mId);
- final String path = cursor.getString(1);
+ final String path = info.mFileName;
if (!TextUtils.isEmpty(path)) {
try {
final File file = new File(path).getCanonicalFile();
@@ -1241,7 +1257,7 @@ public final class DownloadProvider extends ContentProvider {
}
}
- final String mediaUri = cursor.getString(2);
+ final String mediaUri = info.mMediaProviderUri;
if (!TextUtils.isEmpty(mediaUri)) {
final long token = Binder.clearCallingIdentity();
try {
@@ -1251,6 +1267,13 @@ public final class DownloadProvider extends ContentProvider {
Binder.restoreCallingIdentity(token);
}
}
+
+ // If the download wasn't completed yet, we're
+ // effectively completing it now, and we need to send
+ // any requested broadcasts
+ if (!Downloads.Impl.isStatusCompleted(info.mStatus)) {
+ info.sendIntentIfRequested();
+ }
}
}
@@ -1262,6 +1285,12 @@ public final class DownloadProvider extends ContentProvider {
throw new UnsupportedOperationException("Cannot delete URI: " + uri);
}
notifyContentChanged(uri, match);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Helpers.getDownloadNotifier(getContext()).update();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
return count;
}
diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java
index e0bb7cd1..4ec8e2d1 100644
--- a/src/com/android/providers/downloads/DownloadStorageProvider.java
+++ b/src/com/android/providers/downloads/DownloadStorageProvider.java
@@ -29,28 +29,36 @@ import android.net.Uri;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.Environment;
+import android.os.FileObserver;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
+import android.provider.Downloads;
import android.support.provider.DocumentArchiveHelper;
import android.text.TextUtils;
-import android.webkit.MimeTypeMap;
-
-import libcore.io.IoUtils;
+import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.NumberFormat;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
+
+import libcore.io.IoUtils;
+
/**
* Presents a {@link DocumentsContract} view of {@link DownloadManager}
* contents.
*/
public class DownloadStorageProvider extends DocumentsProvider {
+ private static final String TAG = "DownloadStorageProvider";
+ private static final boolean DEBUG = false;
+
private static final String AUTHORITY = Constants.STORAGE_AUTHORITY;
private static final String DOC_ID_ROOT = Constants.STORAGE_ROOT_ID;
@@ -74,6 +82,7 @@ public class DownloadStorageProvider extends DocumentsProvider {
mDm.setAccessAllDownloads(true);
mDm.setAccessFilename(true);
mArchiveHelper = new DocumentArchiveHelper(this, ':');
+
return true;
}
@@ -179,7 +188,8 @@ public class DownloadStorageProvider extends DocumentsProvider {
return mArchiveHelper.queryDocument(docId, projection);
}
- final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+ final DownloadsCursor result =
+ new DownloadsCursor(projection, getContext().getContentResolver());
if (DOC_ID_ROOT.equals(docId)) {
includeDefaultDocument(result);
@@ -200,6 +210,8 @@ public class DownloadStorageProvider extends DocumentsProvider {
Binder.restoreCallingIdentity(token);
}
}
+
+ result.start();
return result;
}
@@ -211,7 +223,8 @@ public class DownloadStorageProvider extends DocumentsProvider {
return mArchiveHelper.queryChildDocuments(docId, projection, sortOrder);
}
- final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+ final DownloadsCursor result =
+ new DownloadsCursor(projection, getContext().getContentResolver());
// Delegate to real provider
final long token = Binder.clearCallingIdentity();
@@ -227,6 +240,8 @@ public class DownloadStorageProvider extends DocumentsProvider {
IoUtils.closeQuietly(cursor);
Binder.restoreCallingIdentity(token);
}
+
+ result.start();
return result;
}
@@ -238,7 +253,8 @@ public class DownloadStorageProvider extends DocumentsProvider {
return mArchiveHelper.queryDocument(parentDocumentId, projection);
}
- final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+ final DownloadsCursor result =
+ new DownloadsCursor(projection, getContext().getContentResolver());
// Delegate to real provider
final long token = Binder.clearCallingIdentity();
@@ -254,13 +270,17 @@ public class DownloadStorageProvider extends DocumentsProvider {
IoUtils.closeQuietly(cursor);
Binder.restoreCallingIdentity(token);
}
+
+ result.start();
return result;
}
@Override
public Cursor queryRecentDocuments(String rootId, String[] projection)
throws FileNotFoundException {
- final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+
+ final DownloadsCursor result =
+ new DownloadsCursor(projection, getContext().getContentResolver());
// Delegate to real provider
final long token = Binder.clearCallingIdentity();
@@ -288,6 +308,8 @@ public class DownloadStorageProvider extends DocumentsProvider {
IoUtils.closeQuietly(cursor);
Binder.restoreCallingIdentity(token);
}
+
+ result.start();
return result;
}
@@ -325,6 +347,10 @@ public class DownloadStorageProvider extends DocumentsProvider {
Document.FLAG_DIR_PREFERS_LAST_MODIFIED | Document.FLAG_DIR_SUPPORTS_CREATE);
}
+ /**
+ * Adds the entry from the cursor to the result only if the entry is valid. That is,
+ * if the file exists in the file system.
+ */
private void includeDownloadFromCursor(MatrixCursor result, Cursor cursor) {
final long id = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID));
final String docId = String.valueOf(id);
@@ -344,12 +370,20 @@ public class DownloadStorageProvider extends DocumentsProvider {
if (size == -1) {
size = null;
}
+ String localFilePath = cursor.getString(
+ cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME));
int extraFlags = Document.FLAG_PARTIAL;
final int status = cursor.getInt(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
switch (status) {
case DownloadManager.STATUS_SUCCESSFUL:
+ // Verify that the document still exists in external storage. This is necessary
+ // because files can be deleted from the file system without their entry being
+ // removed from DownloadsManager.
+ if (localFilePath == null || !new File(localFilePath).exists()) {
+ return;
+ }
extraFlags = Document.FLAG_SUPPORTS_RENAME; // only successful is non-partial
break;
case DownloadManager.STATUS_PAUSED:
@@ -400,38 +434,97 @@ public class DownloadStorageProvider extends DocumentsProvider {
row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
}
- final String localFilePath = cursor.getString(
- cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME));
if (localFilePath != null) {
row.add(DocumentArchiveHelper.COLUMN_LOCAL_FILE_PATH, localFilePath);
}
}
/**
- * Remove file extension from name, but only if exact MIME type mapping
- * exists. This means we can reapply the extension later.
+ * A MatrixCursor that spins up a file observer when the first instance is
+ * started ({@link #start()}, and stops the file observer when the last instance
+ * closed ({@link #close()}. When file changes are observed, a content change
+ * notification is sent on the Downloads content URI.
+ *
+ * <p>This is necessary as other processes, like ExternalStorageProvider,
+ * can access and modify files directly (without sending operations
+ * through DownloadStorageProvider).
+ *
+ * <p>Without this, contents accessible by one a Downloads cursor instance
+ * (like the Downloads root in Files app) can become state.
*/
- private static String removeExtension(String mimeType, String name) {
- final int lastDot = name.lastIndexOf('.');
- if (lastDot >= 0) {
- final String extension = name.substring(lastDot + 1);
- final String nameMime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
- if (mimeType.equals(nameMime)) {
- return name.substring(0, lastDot);
+ private static final class DownloadsCursor extends MatrixCursor {
+
+ private static final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private static int mOpenCursorCount = 0;
+ @GuardedBy("mLock")
+ private static @Nullable ContentChangedRelay mFileWatcher;
+
+ private final ContentResolver mResolver;
+
+ DownloadsCursor(String[] projection, ContentResolver resolver) {
+ super(resolveDocumentProjection(projection));
+ mResolver = resolver;
+ }
+
+ void start() {
+ synchronized (mLock) {
+ if (mOpenCursorCount++ == 0) {
+ mFileWatcher = new ContentChangedRelay(mResolver);
+ mFileWatcher.startWatching();
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ synchronized (mLock) {
+ if (--mOpenCursorCount == 0) {
+ mFileWatcher.stopWatching();
+ mFileWatcher = null;
+ }
}
}
- return name;
}
/**
- * Add file extension to name, but only if exact MIME type mapping exists.
+ * A file observer that notifies on the Downloads content URI(s) when
+ * files change on disk.
*/
- private static String addExtension(String mimeType, String name) {
- final String extension = MimeTypeMap.getSingleton()
- .getExtensionFromMimeType(mimeType);
- if (extension != null) {
- return name + "." + extension;
+ private static class ContentChangedRelay extends FileObserver {
+ private static final int NOTIFY_EVENTS = ATTRIB | CLOSE_WRITE | MOVED_FROM | MOVED_TO
+ | CREATE | DELETE | DELETE_SELF | MOVE_SELF;
+
+ private static final String DOWNLOADS_PATH =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+ .getAbsolutePath();
+ private final ContentResolver mResolver;
+
+ public ContentChangedRelay(ContentResolver resolver) {
+ super(DOWNLOADS_PATH, NOTIFY_EVENTS);
+ mResolver = resolver;
+ }
+
+ @Override
+ public void startWatching() {
+ super.startWatching();
+ if (DEBUG) Log.d(TAG, "Started watching for file changes in: " + DOWNLOADS_PATH);
+ }
+
+ @Override
+ public void stopWatching() {
+ super.stopWatching();
+ if (DEBUG) Log.d(TAG, "Stopped watching for file changes in: " + DOWNLOADS_PATH);
+ }
+
+ @Override
+ public void onEvent(int event, String path) {
+ if ((event & NOTIFY_EVENTS) != 0) {
+ if (DEBUG) Log.v(TAG, "Change detected at path: " + DOWNLOADS_PATH);
+ mResolver.notifyChange(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, null, false);
+ mResolver.notifyChange(Downloads.Impl.CONTENT_URI, null, false);
+ }
}
- return name;
}
}
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index 40194038..c6b4c71a 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -382,7 +382,6 @@ public class DownloadThread extends Thread {
}
if (Downloads.Impl.isStatusCompleted(mInfoDelta.mStatus)) {
- mInfo.sendIntentIfRequested();
if (mInfo.shouldScanFile(mInfoDelta.mStatus)) {
DownloadScanner.requestScanBlocking(mContext, mInfo.mId, mInfoDelta.mFileName,
mInfoDelta.mMimeType);
diff --git a/src/com/android/providers/downloads/OpenHelper.java b/src/com/android/providers/downloads/OpenHelper.java
index 27ab86b9..69a44922 100644
--- a/src/com/android/providers/downloads/OpenHelper.java
+++ b/src/com/android/providers/downloads/OpenHelper.java
@@ -17,10 +17,10 @@
package com.android.providers.downloads;
import static android.app.DownloadManager.COLUMN_LOCAL_FILENAME;
-import static android.app.DownloadManager.COLUMN_LOCAL_URI;
import static android.app.DownloadManager.COLUMN_MEDIA_TYPE;
import static android.app.DownloadManager.COLUMN_URI;
import static android.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI;
+
import static com.android.providers.downloads.Constants.TAG;
import android.app.DownloadManager;
@@ -30,7 +30,6 @@ import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
-import android.os.StrictMode;
import android.provider.DocumentsContract;
import android.provider.Downloads.Impl.RequestHeaders;
import android.util.Log;
@@ -51,14 +50,11 @@ public class OpenHelper {
intent.addFlags(intentFlags);
try {
- StrictMode.disableDeathOnFileUriExposure();
context.startActivity(intent);
return true;
} catch (ActivityNotFoundException e) {
Log.w(TAG, "Failed to start " + intent + ": " + e);
return false;
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
}
}
@@ -78,7 +74,6 @@ public class OpenHelper {
return null;
}
- final Uri localUri = getCursorUri(cursor, COLUMN_LOCAL_URI);
final File file = getCursorFile(cursor, COLUMN_LOCAL_FILENAME);
String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE);
mimeType = DownloadDrmHelper.getOriginalMimeType(context, file, mimeType);
@@ -87,20 +82,16 @@ public class OpenHelper {
Constants.STORAGE_AUTHORITY, String.valueOf(id));
final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(documentUri, mimeType);
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if ("application/vnd.android.package-archive".equals(mimeType)) {
- // PackageInstaller doesn't like content URIs, so open file
- intent.setDataAndType(localUri, mimeType);
-
// Also splice in details about where it came from
final Uri remoteUri = getCursorUri(cursor, COLUMN_URI);
intent.putExtra(Intent.EXTRA_ORIGINATING_URI, remoteUri);
intent.putExtra(Intent.EXTRA_REFERRER, getRefererUri(context, id));
intent.putExtra(Intent.EXTRA_ORIGINATING_UID, getOriginatingUid(context, id));
- } else {
- intent.setDataAndType(documentUri, mimeType);
- intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
return intent;
diff --git a/ui/res/values-my-rMM/strings.xml b/ui/res/values-my-rMM/strings.xml
index 5d286723..ba7bfcc5 100644
--- a/ui/res/values-my-rMM/strings.xml
+++ b/ui/res/values-my-rMM/strings.xml
@@ -39,9 +39,9 @@
<string name="dialog_media_not_found" msgid="4468088418758018765">"ဒေါင်းလုဒ် မပြုလုပ်နိုင်ပါ၊ ပြင်ပ မီဒီယာ မရှိပါ။"</string>
<string name="download_no_application_title" msgid="7024782176657362251">"ဖိုင်အား ဖွင့်မရပါ"</string>
<string name="remove_download" msgid="6372920256257247857">"ဖယ်ရှားရန်"</string>
- <string name="delete_download" msgid="76629022653866471">"ဖျက်ပစ်ရန်"</string>
+ <string name="delete_download" msgid="76629022653866471">"ဖျက်ရန်"</string>
<string name="keep_queued_download" msgid="5144882786014818569">"သိမ်းထားပါ"</string>
- <string name="cancel_running_download" msgid="5232704030969221112">"မလုပ်တော့ပါ"</string>
+ <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>