summaryrefslogtreecommitdiffstats
path: root/src/com/android/providers/downloads/DownloadService.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/providers/downloads/DownloadService.java')
-rw-r--r--src/com/android/providers/downloads/DownloadService.java124
1 files changed, 108 insertions, 16 deletions
diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java
index 8fbcf4b4..0850af74 100644
--- a/src/com/android/providers/downloads/DownloadService.java
+++ b/src/com/android/providers/downloads/DownloadService.java
@@ -27,6 +27,7 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.database.ContentObserver;
import android.database.Cursor;
+import android.media.IMediaScannerListener;
import android.media.IMediaScannerService;
import android.net.Uri;
import android.os.Environment;
@@ -35,12 +36,14 @@ import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.provider.Downloads;
+import android.text.TextUtils;
import android.util.Log;
import com.google.android.collect.Maps;
import com.google.common.annotations.VisibleForTesting;
import java.io.File;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
@@ -94,6 +97,18 @@ public class DownloadService extends Service {
@VisibleForTesting
SystemFacade mSystemFacade;
+ /** Before calling
+ * {@link IMediaScannerService#requestScanFile(String, String, IMediaScannerListener)},
+ * a (key,value) pair of (filepath, uri-to-update-the-row-in-dowloads-db)
+ * is stored in the following struct.
+ * When the callback from
+ * {@link IMediaScannerService#requestScanFile(String, String, IMediaScannerListener)} is
+ * received with params (filepath, uri-to-update-the-row-in-mediaprovider-db),
+ * this struct comes in handy to locate the row in downloads table whose mediaprovier-uri
+ * column is to be updated with the value returned by this callback method.
+ */
+ private final HashMap<String, Uri> downloadsToBeUpdated = new HashMap<String, Uri>();
+
/**
* Receives notifications when the data in the content provider changes
*/
@@ -125,17 +140,20 @@ public class DownloadService extends Service {
if (Constants.LOGVV) {
Log.v(Constants.TAG, "Connected to Media Scanner");
}
- mMediaScannerConnecting = false;
synchronized (DownloadService.this) {
+ mMediaScannerConnecting = false;
mMediaScannerService = IMediaScannerService.Stub.asInterface(service);
if (mMediaScannerService != null) {
updateFromProvider();
}
+ // notify anyone waiting on successful connection to MediaService
+ DownloadService.this.notifyAll();
}
}
public void disconnectMediaScanner() {
synchronized (DownloadService.this) {
+ mMediaScannerConnecting = false;
if (mMediaScannerService != null) {
mMediaScannerService = null;
if (Constants.LOGVV) {
@@ -144,11 +162,11 @@ public class DownloadService extends Service {
try {
unbindService(this);
} catch (IllegalArgumentException ex) {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "unbindService threw up: " + ex);
- }
+ Log.w(Constants.TAG, "unbindService failed: " + ex);
}
}
+ // notify anyone waiting on unsuccessful connection to MediaService
+ DownloadService.this.notifyAll();
}
}
@@ -158,6 +176,9 @@ public class DownloadService extends Service {
}
synchronized (DownloadService.this) {
mMediaScannerService = null;
+ mMediaScannerConnecting = false;
+ // notify anyone waiting on disconnect from MediaService
+ DownloadService.this.notifyAll();
}
}
}
@@ -315,13 +336,48 @@ public class DownloadService extends Service {
deleteDownload(id);
}
+ // is there a need to start the DownloadService? yes, if there are rows to be
+ // deleted.
+ if (!mustScan) {
+ for (DownloadInfo info : mDownloads.values()) {
+ if (info.mDeleted && TextUtils.isEmpty(info.mMediaProviderUri)) {
+ mustScan = true;
+ keepService = true;
+ break;
+ }
+ }
+ }
mNotifier.updateNotification(mDownloads.values());
-
if (mustScan) {
bindMediaScanner();
} else {
mMediaScannerConnection.disconnectMediaScanner();
}
+
+ // look for all rows with deleted flag set and delete the rows from the database
+ // permanently
+ for (DownloadInfo info : mDownloads.values()) {
+ if (info.mDeleted) {
+ // this row is to be deleted from the database. but does it have
+ // mediaProviderUri?
+ if (TextUtils.isEmpty(info.mMediaProviderUri)) {
+ // initiate rescan of the file to - which will populate mediaProviderUri
+ // column in this row
+ if (!scanFile(info, true)) {
+ throw new IllegalStateException("scanFile failed!");
+ }
+ } else {
+ // yes it has mediaProviderUri column already filled in.
+ // delete it from MediaProvider database and then from downloads table
+ // in DownProvider database (the order of deletion is important).
+ getContentResolver().delete(Uri.parse(info.mMediaProviderUri), null,
+ null);
+ getContentResolver().delete(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
+ Downloads.Impl._ID + " = ? ",
+ new String[]{String.valueOf(info.mId)});
+ }
+ }
+ }
}
}
@@ -491,24 +547,60 @@ public class DownloadService extends Service {
private boolean scanFile(DownloadInfo info, boolean updateDatabase) {
synchronized (this) {
if (mMediaScannerService == null) {
+ // not bound to mediaservice. but if in the process of connecting to it, wait until
+ // connection is resolved
+ while (mMediaScannerConnecting) {
+ Log.d(Constants.TAG, "waiting for mMediaScannerService service: ");
+ try {
+ this.wait();
+ } catch (InterruptedException e1) {
+ throw new IllegalStateException("wait interrupted");
+ }
+ }
+ }
+ // do we have mediaservice?
+ if (mMediaScannerService == null) {
+ // no available MediaService And not even in the process of connecting to it
return false;
}
- try {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "Scanning file " + info.mFileName);
- }
- mMediaScannerService.scanFile(info.mFileName, info.mMimeType);
- if (updateDatabase) {
- ContentValues values = new ContentValues();
- values.put(Constants.MEDIA_SCANNED, 1);
- getContentResolver().update(info.getAllDownloadsUri(), values, null, null);
+ if (Constants.LOGV) {
+ Log.v(Constants.TAG, "Scanning file " + info.mFileName);
+ }
+ if (updateDatabase) {
+ synchronized(downloadsToBeUpdated) {
+ Uri value = info.getAllDownloadsUri();
+ if (value != null) {
+ downloadsToBeUpdated.put(info.mFileName, value);
+ }
}
+ }
+ try {
+ mMediaScannerService.requestScanFile(info.mFileName, info.mMimeType,
+ new IMediaScannerListener.Stub() {
+ public void scanCompleted(String path, Uri uri) {
+ Uri key;
+ boolean updateMediaproviderUriColumn;
+ synchronized(downloadsToBeUpdated) {
+ key = downloadsToBeUpdated.get(path);
+ updateMediaproviderUriColumn = (key != null);
+ }
+ if (updateMediaproviderUriColumn) {
+ ContentValues values = new ContentValues();
+ values.put(Constants.MEDIA_SCANNED, 1);
+ values.put(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI,
+ uri.toString());
+ getContentResolver().update(key, values, null, null);
+ synchronized(downloadsToBeUpdated) {
+ downloadsToBeUpdated.remove(path);
+ }
+ }
+ }
+ });
return true;
} catch (RemoteException e) {
- Log.d(Constants.TAG, "Failed to scan file " + info.mFileName);
+ Log.w(Constants.TAG, "Failed to scan file " + info.mFileName);
return false;
}
}
}
-
}