Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DownloadManager doesn't send broadcast after INSUFFICIENT_SPACE_ERROR

The problem

If the cache directory is full, trying to execute a simple request will fail without sending the DownloadManager.ACTION_DOWNLOAD_COMPLETE broadcast.

Note: The problem is general but can be mostly reproduced on low-end devices with limited cache (/data/data/com.android.providers.downloads/cache) size.

The code

The receiver is configured correctly, as I'm still getting the broadcast when the operation succeeds and fails for other reasons.

    DownloadManager.Request request = new DownloadManager.Request(Uri.parse("http://www.apkmirror.com/wp-content/themes/APKMirror/download.php?id=44753"));

    request.setTitle("Facebook");

    DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);

    downloadManager.enqueue(request);

The desired solution

I'm interested in a solution to the specific issue, or more info if you have encountered it as well.
I'm not looking for a solution that will require me to stop using the DownloadManager or add the WRITE_EXTERNAL_STORAGE permission.

Logs

When the cache is getting full and lastly when it can hold no more you can observe the following log entrance (filtered with downloadmanager)

11-08 08:47:06.079 830-14261/? I/DownloadManager: Download 135 starting
11-08 08:47:06.989 830-14261/? W/DownloadManager: Downloads data dir: /data/data/com.android.providers.downloads/cache is running low on space. space available (in bytes): -6994124
11-08 08:47:06.999 830-14261/? I/DownloadManager: discardPurgeableFiles: destination = 2, targetBytes = 10485760
11-08 08:47:06.999 830-14261/? I/DownloadManager: Purged files, freed 0 for 10485760 requested
11-08 08:47:07.309 830-14261/? W/DownloadManager: Aborting request for download 135: not enough free space in the filesystem rooted at: /data/data/com.android.providers.downloads/cache and unable to free any more
11-08 08:47:07.319 830-14261/? I/DownloadManager: Download 135 finished with status INSUFFICIENT_SPACE_ERROR

Here is a DEMO PROJECT that can demonstrate the issue. Remember that the cache directory has to be full by that point (by non-purgeable items, which from my experience basically means, aborted downloads)

like image 901
Alex.F Avatar asked Nov 08 '15 15:11

Alex.F


1 Answers

Since DownloadManager is a system ContentProvider in essence, you can register your own ContentObserver to it, So when the download provider updates, it'll choose to notify the observer in the case of INSUFFICIENT_SPACE.

final DownloadManager downloadManager = (DownloadManager)context.getSystemService(Context.DOWNLOAD_SERVICE);
        context.getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"),
                true, new ContentObserver(null) {
                    @Override
                    public void onChange(boolean selfChange) {
                        super.onChange(selfChange);
                        Cursor localCursor = downloadManager.query(
                                new DownloadManager.Query());
                        if (localCursor.getCount() == 0) {
                            localCursor.close();
                        }
                        localCursor.moveToFirst();
                        do {
                            if ((localCursor.getInt(localCursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) & DownloadManager.STATUS_FAILED )!=0) {
                                // Download failed, go see why
                                if (localCursor.getInt(localCursor.getColumnIndex(DownloadManager.COLUMN_REASON)) == DownloadManager.ERROR_INSUFFICIENT_SPACE){
                                    Log.w("DownloadStatus", " Download failed with ERROR_INSUFFICIENT_SPACE");
                                }
                            }
                        }while (localCursor.moveToNext());
                    }
                });

Note that don't set query filter with status DownloadManager.STATUS_FAILED, as DownloadManager weirdly only consider status between 400 and 600 to be failed status, but INSUFFICIENT_SPACE has error code 198...

android.app.DownloadManager.Request:

Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) {
 .....
 if ((mStatusFlags & STATUS_FAILED) != 0) {
                    parts.add("(" + statusClause(">=", 400)
                              + " AND " + statusClause("<", 600) + ")");
                }
}
like image 193
Gracie Avatar answered Oct 09 '22 17:10

Gracie