diff options
Diffstat (limited to 'src/com')
8 files changed, 176 insertions, 177 deletions
diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java index 5cf13531..da2c2fd9 100644 --- a/src/com/android/providers/downloads/Constants.java +++ b/src/com/android/providers/downloads/Constants.java @@ -16,7 +16,6 @@ package com.android.providers.downloads; -import android.util.Config; import android.util.Log; /** @@ -144,11 +143,10 @@ public class Constants { static final boolean LOGX = false; /** Enable verbose logging - use with "setprop log.tag.DownloadManager VERBOSE" */ - private static final boolean LOCAL_LOGV = false; - public static final boolean LOGV = Config.LOGV - || (Config.LOGD && LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE)); + private static final boolean LOCAL_LOGV = true; // STOPSHIP change this to false before shipping + public static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE); /** Enable super-verbose logging */ - private static final boolean LOCAL_LOGVV = false; + private static final boolean LOCAL_LOGVV = true; // STOPSHIP change this to false before shipping public static final boolean LOGVV = LOCAL_LOGVV && LOGV; } diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java index 36816b59..363b68cd 100644 --- a/src/com/android/providers/downloads/DownloadInfo.java +++ b/src/com/android/providers/downloads/DownloadInfo.java @@ -45,8 +45,6 @@ public class DownloadInfo { public static class Reader { private ContentResolver mResolver; private Cursor mCursor; - private CharArrayBuffer mOldChars; - private CharArrayBuffer mNewChars; public Reader(ContentResolver resolver, Cursor cursor) { mResolver = resolver; @@ -62,11 +60,11 @@ public class DownloadInfo { public void updateFromDatabase(DownloadInfo info) { info.mId = getLong(Downloads.Impl._ID); - info.mUri = getString(info.mUri, Downloads.Impl.COLUMN_URI); + info.mUri = getString(Downloads.Impl.COLUMN_URI); info.mNoIntegrity = getInt(Downloads.Impl.COLUMN_NO_INTEGRITY) == 1; - info.mHint = getString(info.mHint, Downloads.Impl.COLUMN_FILE_NAME_HINT); - info.mFileName = getString(info.mFileName, Downloads.Impl._DATA); - info.mMimeType = getString(info.mMimeType, Downloads.Impl.COLUMN_MIME_TYPE); + info.mHint = getString(Downloads.Impl.COLUMN_FILE_NAME_HINT); + info.mFileName = getString(Downloads.Impl._DATA); + info.mMimeType = getString(Downloads.Impl.COLUMN_MIME_TYPE); info.mDestination = getInt(Downloads.Impl.COLUMN_DESTINATION); info.mVisibility = getInt(Downloads.Impl.COLUMN_VISIBILITY); info.mStatus = getInt(Downloads.Impl.COLUMN_STATUS); @@ -75,24 +73,23 @@ public class DownloadInfo { info.mRetryAfter = retryRedirect & 0xfffffff; info.mRedirectCount = retryRedirect >> 28; info.mLastMod = getLong(Downloads.Impl.COLUMN_LAST_MODIFICATION); - info.mPackage = getString(info.mPackage, Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE); - info.mClass = getString(info.mClass, Downloads.Impl.COLUMN_NOTIFICATION_CLASS); - info.mExtras = getString(info.mExtras, Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS); - info.mCookies = getString(info.mCookies, Downloads.Impl.COLUMN_COOKIE_DATA); - info.mUserAgent = getString(info.mUserAgent, Downloads.Impl.COLUMN_USER_AGENT); - info.mReferer = getString(info.mReferer, Downloads.Impl.COLUMN_REFERER); + info.mPackage = getString(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE); + info.mClass = getString(Downloads.Impl.COLUMN_NOTIFICATION_CLASS); + info.mExtras = getString(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS); + info.mCookies = getString(Downloads.Impl.COLUMN_COOKIE_DATA); + info.mUserAgent = getString(Downloads.Impl.COLUMN_USER_AGENT); + info.mReferer = getString(Downloads.Impl.COLUMN_REFERER); info.mTotalBytes = getLong(Downloads.Impl.COLUMN_TOTAL_BYTES); info.mCurrentBytes = getLong(Downloads.Impl.COLUMN_CURRENT_BYTES); - info.mETag = getString(info.mETag, Constants.ETAG); + info.mETag = getString(Constants.ETAG); info.mMediaScanned = getInt(Constants.MEDIA_SCANNED) == 1; info.mDeleted = getInt(Downloads.Impl.COLUMN_DELETED) == 1; - info.mMediaProviderUri = getString(info.mMediaProviderUri, - Downloads.Impl.COLUMN_MEDIAPROVIDER_URI); + info.mMediaProviderUri = getString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI); info.mIsPublicApi = getInt(Downloads.Impl.COLUMN_IS_PUBLIC_API) != 0; info.mAllowedNetworkTypes = getInt(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES); info.mAllowRoaming = getInt(Downloads.Impl.COLUMN_ALLOW_ROAMING) != 0; - info.mTitle = getString(info.mTitle, Downloads.Impl.COLUMN_TITLE); - info.mDescription = getString(info.mDescription, Downloads.Impl.COLUMN_DESCRIPTION); + info.mTitle = getString(Downloads.Impl.COLUMN_TITLE); + info.mDescription = getString(Downloads.Impl.COLUMN_DESCRIPTION); info.mBypassRecommendedSizeLimit = getInt(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT); @@ -130,35 +127,10 @@ public class DownloadInfo { info.mRequestHeaders.add(Pair.create(header, value)); } - /** - * Returns a String that holds the current value of the column, optimizing for the case - * where the value hasn't changed. - */ - private String getString(String old, String column) { + private String getString(String column) { int index = mCursor.getColumnIndexOrThrow(column); - if (old == null) { - return mCursor.getString(index); - } - if (mNewChars == null) { - mNewChars = new CharArrayBuffer(128); - } - mCursor.copyStringToBuffer(index, mNewChars); - int length = mNewChars.sizeCopied; - if (length != old.length()) { - return new String(mNewChars.data, 0, length); - } - if (mOldChars == null || mOldChars.sizeCopied < length) { - mOldChars = new CharArrayBuffer(length); - } - char[] oldArray = mOldChars.data; - char[] newArray = mNewChars.data; - old.getChars(0, length, oldArray, 0); - for (int i = length - 1; i >= 0; --i) { - if (oldArray[i] != newArray[i]) { - return new String(newArray, 0, length); - } - } - return old; + String s = mCursor.getString(index); + return (TextUtils.isEmpty(s)) ? null : s; } private Integer getInt(String column) { @@ -547,7 +519,8 @@ public class DownloadInfo { */ boolean shouldScanFile() { return !mMediaScanned - && mDestination == Downloads.Impl.DESTINATION_EXTERNAL + && (mDestination == Downloads.Impl.DESTINATION_EXTERNAL || + mDestination == Downloads.Impl.DESTINATION_FILE_URI) && Downloads.Impl.isStatusSuccess(mStatus) && !DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mMimeType); } diff --git a/src/com/android/providers/downloads/DownloadNotification.java b/src/com/android/providers/downloads/DownloadNotification.java index 4d615df7..4002a97a 100644 --- a/src/com/android/providers/downloads/DownloadNotification.java +++ b/src/com/android/providers/downloads/DownloadNotification.java @@ -259,12 +259,12 @@ class DownloadNotification { private boolean isActiveAndVisible(DownloadInfo download) { return 100 <= download.mStatus && download.mStatus < 200 - && download.mVisibility != Downloads.VISIBILITY_HIDDEN; + && download.mVisibility != Downloads.Impl.VISIBILITY_HIDDEN; } private boolean isCompleteAndVisible(DownloadInfo download) { return download.mStatus >= 200 - && download.mVisibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED; + && download.mVisibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED; } /* diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java index d97d6189..9336b737 100644 --- a/src/com/android/providers/downloads/DownloadProvider.java +++ b/src/com/android/providers/downloads/DownloadProvider.java @@ -16,6 +16,7 @@ package com.android.providers.downloads; +import android.app.DownloadManager; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; @@ -25,10 +26,8 @@ import android.content.UriMatcher; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.database.CrossProcessCursor; import android.database.Cursor; -import android.database.CursorWindow; -import android.database.CursorWrapper; +import android.database.DatabaseUtils; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; @@ -45,11 +44,11 @@ import com.google.common.annotations.VisibleForTesting; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; /** @@ -59,7 +58,7 @@ public final class DownloadProvider extends ContentProvider { /** Database filename */ private static final String DB_NAME = "downloads.db"; /** Current database version */ - private static final int DB_VERSION = 106; + private static final int DB_VERSION = 107; /** Name of table in the database */ private static final String DB_TABLE = "downloads"; @@ -80,6 +79,11 @@ public final class DownloadProvider extends ContentProvider { private static final int ALL_DOWNLOADS_ID = 4; /** URI matcher constant for the URI of a download's request headers */ private static final int REQUEST_HEADERS_URI = 5; + /** URI matcher constant for the public URI returned by + * {@link DownloadManager#getUriForDownloadedFile(long)} if the given downloaded file + * is publicly accessible. + */ + private static final int PUBLIC_DOWNLOAD_ID = 6; static { sURIMatcher.addURI("downloads", "my_downloads", MY_DOWNLOADS); sURIMatcher.addURI("downloads", "my_downloads/#", MY_DOWNLOADS_ID); @@ -97,6 +101,9 @@ public final class DownloadProvider extends ContentProvider { sURIMatcher.addURI("downloads", "download/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT, REQUEST_HEADERS_URI); + sURIMatcher.addURI("downloads", + Downloads.Impl.PUBLICLY_ACCESSIBLE_DOWNLOADS_URI_SEGMENT + "/#", + PUBLIC_DOWNLOAD_ID); } /** Different base URIs that could be used to access an individual download */ @@ -135,6 +142,8 @@ public final class DownloadProvider extends ContentProvider { sAppReadableColumnsSet.add(sAppReadableColumnsArray[i]); } } + private static final List<String> downloadManagerColumnsList = + Arrays.asList(DownloadManager.UNDERLYING_COLUMNS); /** The database that lies underneath this content provider */ private SQLiteOpenHelper mOpenHelper = null; @@ -279,6 +288,10 @@ public final class DownloadProvider extends ContentProvider { "BOOLEAN NOT NULL DEFAULT 0"); break; + case 107: + addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ERROR_MSG, "TEXT"); + break; + default: throw new IllegalStateException("Don't know how to upgrade to " + version); } @@ -425,6 +438,15 @@ public final class DownloadProvider extends ContentProvider { case MY_DOWNLOADS_ID: { return DOWNLOAD_TYPE; } + case PUBLIC_DOWNLOAD_ID: { + // return the mimetype of this id from the database + final String id = getDownloadIdFromUri(uri); + final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + return DatabaseUtils.stringForQuery(db, + "SELECT " + Downloads.Impl.COLUMN_MIME_TYPE + " FROM " + DB_TABLE + + " WHERE " + Downloads.Impl._ID + " = ?", + new String[]{id}); + } default: { if (Constants.LOGV) { Log.v(Constants.TAG, "calling getType on an unknown URI: " + uri); @@ -645,6 +667,7 @@ public final class DownloadProvider extends ContentProvider { values.remove(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES); values.remove(Downloads.Impl.COLUMN_ALLOW_ROAMING); values.remove(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI); + values.remove(Downloads.Impl.COLUMN_MEDIA_SCANNED); Iterator<Map.Entry<String, Object>> iterator = values.valueSet().iterator(); while (iterator.hasNext()) { String key = iterator.next().getKey(); @@ -720,8 +743,10 @@ public final class DownloadProvider extends ContentProvider { if (projection == null) { projection = sAppReadableColumnsArray; } else { + // check the validity of the columns in projection for (int i = 0; i < projection.length; ++i) { - if (!sAppReadableColumnsSet.contains(projection[i])) { + if (!sAppReadableColumnsSet.contains(projection[i]) && + !downloadManagerColumnsList.contains(projection[i])) { throw new IllegalArgumentException( "column " + projection[i] + " is not allowed in queries"); } @@ -737,10 +762,6 @@ public final class DownloadProvider extends ContentProvider { fullSelection.getParameters(), null, null, sort); if (ret != null) { - ret = new ReadOnlyCursorWrapper(ret); - } - - if (ret != null) { ret.setNotificationUri(getContext().getContentResolver(), uri); if (Constants.LOGVV) { Log.v(Constants.TAG, @@ -831,9 +852,8 @@ public final class DownloadProvider extends ContentProvider { + getDownloadIdFromUri(uri); String[] projection = new String[] {Downloads.Impl.RequestHeaders.COLUMN_HEADER, Downloads.Impl.RequestHeaders.COLUMN_VALUE}; - Cursor cursor = db.query(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, projection, where, - null, null, null, null); - return new ReadOnlyCursorWrapper(cursor); + return db.query(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, projection, where, + null, null, null, null); } /** @@ -972,7 +992,8 @@ public final class DownloadProvider extends ContentProvider { int uriMatch) { SqlSelection selection = new SqlSelection(); selection.appendClause(where, whereArgs); - if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID) { + if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID || + uriMatch == PUBLIC_DOWNLOAD_ID) { selection.appendClause(Downloads.Impl._ID + " = ?", getDownloadIdFromUri(uri)); } if ((uriMatch == MY_DOWNLOADS || uriMatch == MY_DOWNLOADS_ID) @@ -1128,34 +1149,4 @@ public final class DownloadProvider extends ContentProvider { to.put(key, defaultValue); } } - - private class ReadOnlyCursorWrapper extends CursorWrapper implements CrossProcessCursor { - public ReadOnlyCursorWrapper(Cursor cursor) { - super(cursor); - mCursor = (CrossProcessCursor) cursor; - } - - public boolean deleteRow() { - throw new SecurityException("Download manager cursors are read-only"); - } - - public boolean commitUpdates() { - throw new SecurityException("Download manager cursors are read-only"); - } - - public void fillWindow(int pos, CursorWindow window) { - mCursor.fillWindow(pos, window); - } - - public CursorWindow getWindow() { - return mCursor.getWindow(); - } - - public boolean onMove(int oldPosition, int newPosition) { - return mCursor.onMove(oldPosition, newPosition); - } - - private CrossProcessCursor mCursor; - } - } diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java index c41895b9..33066393 100644 --- a/src/com/android/providers/downloads/DownloadReceiver.java +++ b/src/com/android/providers/downloads/DownloadReceiver.java @@ -41,6 +41,7 @@ public class DownloadReceiver extends BroadcastReceiver { @VisibleForTesting SystemFacade mSystemFacade = null; + @Override public void onReceive(Context context, Intent intent) { if (mSystemFacade == null) { mSystemFacade = new RealSystemFacade(context); @@ -169,11 +170,21 @@ public class DownloadReceiver extends BroadcastReceiver { if (isPublicApi) { appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED); appIntent.setPackage(pckg); + // send id of the items clicked on. + if (intent.getBooleanExtra("multiple", false)) { + // broadcast received saying click occurred on a notification with multiple titles. + // don't include any ids at all - let the caller query all downloads belonging to it + // TODO modify the broadcast to include ids of those multiple notifications. + } else { + appIntent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, + new long[] { + cursor.getLong(cursor.getColumnIndexOrThrow(Downloads.Impl._ID))}); + } } else { // legacy behavior if (clazz == null) { return; } - appIntent = new Intent(Downloads.Impl.ACTION_NOTIFICATION_CLICKED); + appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED); appIntent.setClassName(pckg, clazz); if (intent.getBooleanExtra("multiple", true)) { appIntent.setData(Downloads.Impl.CONTENT_URI); diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java index 169ef970..f93c5c2e 100644 --- a/src/com/android/providers/downloads/DownloadService.java +++ b/src/com/android/providers/downloads/DownloadService.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.database.ContentObserver; import android.database.Cursor; +import android.database.sqlite.SQLiteException; import android.media.IMediaScannerListener; import android.media.IMediaScannerService; import android.net.Uri; @@ -44,7 +45,6 @@ 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; @@ -114,6 +114,7 @@ public class DownloadService extends Service { * Receives notification when the data in the observed content * provider changes. */ + @Override public void onChange(final boolean selfChange) { if (Constants.LOGVV) { Log.v(Constants.TAG, "Service ContentObserver received notification"); @@ -188,6 +189,7 @@ public class DownloadService extends Service { * * @throws UnsupportedOperationException */ + @Override public IBinder onBind(Intent i) { throw new UnsupportedOperationException("Cannot bind to Download Manager Service"); } @@ -195,6 +197,7 @@ public class DownloadService extends Service { /** * Initializes the service when it is first created */ + @Override public void onCreate() { super.onCreate(); if (Constants.LOGVV) { @@ -232,6 +235,7 @@ public class DownloadService extends Service { /** * Cleans up when the service is destroyed */ + @Override public void onDestroy() { getContentResolver().unregisterContentObserver(mObserver); if (Constants.LOGVV) { @@ -258,6 +262,7 @@ public class DownloadService extends Service { super("Download Service"); } + @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); @@ -363,24 +368,23 @@ public class DownloadService extends Service { if (info.shouldScanFile()) { // initiate rescan of the file to - which will populate // mediaProviderUri column in this row - if (!scanFile(info, true, false)) { + if (!scanFile(info, false, true)) { throw new IllegalStateException("scanFile failed!"); } - } else { - // this file should NOT be scanned. delete the file. - Helpers.deleteFile(getContentResolver(), info.mId, info.mFileName, - info.mMimeType); + continue; } } 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). + // delete it from MediaProvider database. 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)}); } + // delete the file + deleteFileIfExists(info.mFileName); + // delete from the downloads db + getContentResolver().delete(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, + Downloads.Impl._ID + " = ? ", + new String[]{String.valueOf(info.mId)}); } } } @@ -463,29 +467,41 @@ public class DownloadService extends Service { * Drops old rows from the database to prevent it from growing too large */ private void trimDatabase() { - Cursor cursor = getContentResolver().query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, - new String[] { Downloads.Impl._ID }, - Downloads.Impl.COLUMN_STATUS + " >= '200'", null, - Downloads.Impl.COLUMN_LAST_MODIFICATION); - if (cursor == null) { - // This isn't good - if we can't do basic queries in our database, nothing's gonna work - Log.e(Constants.TAG, "null cursor in trimDatabase"); - return; - } - if (cursor.moveToFirst()) { - int numDelete = cursor.getCount() - Constants.MAX_DOWNLOADS; - int columnId = cursor.getColumnIndexOrThrow(Downloads.Impl._ID); - while (numDelete > 0) { - Uri downloadUri = ContentUris.withAppendedId( - Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, cursor.getLong(columnId)); - getContentResolver().delete(downloadUri, null, null); - if (!cursor.moveToNext()) { - break; + Cursor cursor = null; + try { + cursor = getContentResolver().query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, + new String[] { Downloads.Impl._ID }, + Downloads.Impl.COLUMN_STATUS + " >= '200'", null, + Downloads.Impl.COLUMN_LAST_MODIFICATION); + if (cursor == null) { + // This isn't good - if we can't do basic queries in our database, nothing's gonna work + Log.e(Constants.TAG, "null cursor in trimDatabase"); + return; + } + if (cursor.moveToFirst()) { + int numDelete = cursor.getCount() - Constants.MAX_DOWNLOADS; + int columnId = cursor.getColumnIndexOrThrow(Downloads.Impl._ID); + while (numDelete > 0) { + Uri downloadUri = ContentUris.withAppendedId( + Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, cursor.getLong(columnId)); + getContentResolver().delete(downloadUri, null, null); + if (!cursor.moveToNext()) { + break; + } + numDelete--; } - numDelete--; + } + } catch (SQLiteException e) { + // trimming the database raised an exception. alright, ignore the exception + // and return silently. trimming database is not exactly a critical operation + // and there is no need to propagate the exception. + Log.w(Constants.TAG, "trimDatabase failed with exception: " + e.getMessage()); + return; + } finally { + if (cursor != null) { + cursor.close(); } } - cursor.close(); } /** @@ -580,22 +596,28 @@ public class DownloadService extends Service { mMediaScannerService.requestScanFile(info.mFileName, info.mMimeType, new IMediaScannerListener.Stub() { public void scanCompleted(String path, Uri uri) { - if (uri != null && updateDatabase) { - // file is scanned and mediaprovider returned uri. store it in downloads - // table (i.e., update this downloaded file's row) + if (updateDatabase) { + // Mark this as 'scanned' in the database + // so that it is NOT subject to re-scanning by MediaScanner + // next time this database row row is encountered ContentValues values = new ContentValues(); values.put(Constants.MEDIA_SCANNED, 1); - values.put(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, - uri.toString()); + if (uri != null) { + values.put(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, + uri.toString()); + } getContentResolver().update(key, values, null, null); - } else if (uri == null && deleteFile) { - // callback returned NO uri..that means this file doesn't - // exist in MediaProvider. but it still needs to be deleted - // TODO don't scan files that are not scannable by MediaScanner. - // create a public method in MediaFile.java to return false - // if the given file's mimetype is not any of the types - // the mediaprovider is interested in. - Helpers.deleteFile(resolver, id, path, mimeType); + } else if (deleteFile) { + if (uri != null) { + // use the Uri returned to delete it from the MediaProvider + getContentResolver().delete(uri, null, null); + } + // delete the file and delete its row from the downloads db + deleteFileIfExists(path); + getContentResolver().delete( + Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, + Downloads.Impl._ID + " = ? ", + new String[]{String.valueOf(id)}); } } }); @@ -606,4 +628,15 @@ public class DownloadService extends Service { } } } + + private void deleteFileIfExists(String path) { + try { + if (!TextUtils.isEmpty(path)) { + File file = new File(path); + file.delete(); + } + } catch (Exception e) { + Log.w(Constants.TAG, "file: '" + path + "' couldn't be deleted", e); + } + } } diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java index 0da950ba..a1d91019 100644 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.drm.mobile1.DrmRawContent; import android.net.http.AndroidHttpClient; import android.net.Proxy; -import android.net.Uri; import android.os.FileUtils; import android.os.PowerManager; import android.os.Process; @@ -150,6 +149,7 @@ public class DownloadThread extends Thread { AndroidHttpClient client = null; PowerManager.WakeLock wakeLock = null; int finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR; + String errorMsg = null; try { PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -189,11 +189,12 @@ public class DownloadThread extends Thread { finalStatus = Downloads.Impl.STATUS_SUCCESS; } catch (StopRequest error) { // remove the cause before printing, in case it contains PII - Log.w(Constants.TAG, - "Aborting request for download " + mInfo.mId + ": " + error.getMessage()); + errorMsg = "Aborting request for download " + mInfo.mId + ": " + error.getMessage(); + Log.w(Constants.TAG, errorMsg); finalStatus = error.mFinalStatus; // fall through to finally block } catch (Throwable ex) { //sometimes the socket code throws unchecked exceptions + errorMsg = "Exception for id " + mInfo.mId + ": " + ex.getMessage(); Log.w(Constants.TAG, "Exception for id " + mInfo.mId + ": " + ex); finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR; // falls through to the code that reports an error @@ -209,7 +210,7 @@ public class DownloadThread extends Thread { cleanupDestination(state, finalStatus); notifyDownloadCompleted(finalStatus, state.mCountRetry, state.mRetryAfter, state.mRedirectCount, state.mGotData, state.mFilename, - state.mNewUri, state.mMimeType); + state.mNewUri, state.mMimeType, errorMsg); mInfo.mHasActiveThread = false; } } @@ -688,7 +689,8 @@ public class DownloadThread extends Thread { } else { finalStatus = Downloads.Impl.STATUS_UNHANDLED_HTTP_CODE; } - throw new StopRequest(finalStatus, "http error " + statusCode); + throw new StopRequest(finalStatus, "http error " + statusCode + ", mContinuingDownload: " + + innerState.mContinuingDownload); } /** @@ -863,9 +865,10 @@ public class DownloadThread extends Thread { */ private void notifyDownloadCompleted( int status, boolean countRetry, int retryAfter, int redirectCount, boolean gotData, - String filename, String uri, String mimeType) { + String filename, String uri, String mimeType, String errorMsg) { notifyThroughDatabase( - status, countRetry, retryAfter, redirectCount, gotData, filename, uri, mimeType); + status, countRetry, retryAfter, redirectCount, gotData, filename, uri, mimeType, + errorMsg); if (Downloads.Impl.isStatusCompleted(status)) { mInfo.sendIntentIfRequested(); } @@ -873,7 +876,7 @@ public class DownloadThread extends Thread { private void notifyThroughDatabase( int status, boolean countRetry, int retryAfter, int redirectCount, boolean gotData, - String filename, String uri, String mimeType) { + String filename, String uri, String mimeType, String errorMsg) { ContentValues values = new ContentValues(); values.put(Downloads.Impl.COLUMN_STATUS, status); values.put(Downloads.Impl._DATA, filename); @@ -890,7 +893,11 @@ public class DownloadThread extends Thread { } else { values.put(Constants.FAILED_CONNECTIONS, mInfo.mNumFailed + 1); } - + // STOPSHIP begin delete the following lines + if (!TextUtils.isEmpty(errorMsg)) { + values.put(Downloads.Impl.COLUMN_ERROR_MSG, errorMsg); + } + // STOPSHIP end mContext.getContentResolver().update(mInfo.getAllDownloadsUri(), values, null, null); } diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java index a20a7592..59cc97cf 100644 --- a/src/com/android/providers/downloads/Helpers.java +++ b/src/com/android/providers/downloads/Helpers.java @@ -16,7 +16,6 @@ package com.android.providers.downloads; -import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.content.Intent; @@ -99,25 +98,22 @@ public class Helpers { boolean isPublicApi) throws GenerateSaveFileError { checkCanHandleDownload(context, mimeType, destination, isPublicApi); if (destination == Downloads.Impl.DESTINATION_FILE_URI) { - return getPathForFileUri(hint, contentLength); + String path = verifyFileUri(hint, contentLength); + String c = getFullPath(path, mimeType, destination, null); + return c; } else { return chooseFullPath(context, url, hint, contentDisposition, contentLocation, mimeType, destination, contentLength); } } - private static String getPathForFileUri(String hint, long contentLength) + private static String verifyFileUri(String hint, long contentLength) throws GenerateSaveFileError { if (!isExternalMediaMounted()) { throw new GenerateSaveFileError(Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR, "external media not mounted"); } String path = Uri.parse(hint).getPath(); - if (new File(path).exists()) { - Log.d(Constants.TAG, "File already exists: " + path); - throw new GenerateSaveFileError(Downloads.Impl.STATUS_FILE_ALREADY_EXISTS_ERROR, - "requested destination file already exists"); - } if (getAvailableBytes(getFilesystemRoot(path)) < contentLength) { throw new GenerateSaveFileError(Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR, "insufficient space on external storage"); @@ -148,11 +144,15 @@ public class Helpers { File base = locateDestinationDirectory(context, mimeType, destination, contentLength); String filename = chooseFilename(url, hint, contentDisposition, contentLocation, destination); + return getFullPath(filename, mimeType, destination, base); + } + private static String getFullPath(String filename, String mimeType, int destination, + File base) throws GenerateSaveFileError { // Split filename between base and extension // Add an extension if filename does not have one String extension = null; - int dotIndex = filename.indexOf('.'); + int dotIndex = filename.lastIndexOf('.'); if (dotIndex < 0) { extension = chooseExtensionFromMimeType(mimeType, true); } else { @@ -162,12 +162,13 @@ public class Helpers { boolean recoveryDir = Constants.RECOVERY_DIRECTORY.equalsIgnoreCase(filename + extension); - filename = base.getPath() + File.separator + filename; + if (base != null) { + filename = base.getPath() + File.separator + filename; + } if (Constants.LOGVV) { Log.v(Constants.TAG, "target file: " + filename + extension); } - return chooseUniqueFilename(destination, filename, extension, recoveryDir); } @@ -850,19 +851,4 @@ public class Helpers { } return sb.toString(); } - - /* - * Delete the given file from device - * and delete its row from the downloads database. - */ - /* package */ static void deleteFile(ContentResolver resolver, long id, String path, String mimeType) { - try { - File file = new File(path); - file.delete(); - } catch (Exception e) { - Log.w(Constants.TAG, "file: '" + path + "' couldn't be deleted", e); - } - resolver.delete(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, Downloads.Impl._ID + " = ? ", - new String[]{String.valueOf(id)}); - } } |