diff options
Diffstat (limited to 'src/com/android/providers')
9 files changed, 284 insertions, 179 deletions
diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java index 977f00b9..8481435f 100644 --- a/src/com/android/providers/downloads/Constants.java +++ b/src/com/android/providers/downloads/Constants.java @@ -16,7 +16,9 @@ package com.android.providers.downloads; +import android.os.Build; import android.os.Environment; +import android.text.TextUtils; import android.util.Log; /** @@ -83,13 +85,40 @@ public class Constants { public static final String DEFAULT_DL_SUBDIR = "/" + Environment.DIRECTORY_DOWNLOADS; /** A magic filename that is allowed to exist within the system cache */ - public static final String KNOWN_SPURIOUS_FILENAME = "lost+found"; - - /** A magic filename that is allowed to exist within the system cache */ public static final String RECOVERY_DIRECTORY = "recovery"; /** The default user agent used for downloads */ - public static final String DEFAULT_USER_AGENT = "AndroidDownloadManager"; + public static final String DEFAULT_USER_AGENT; + + static { + final StringBuilder builder = new StringBuilder(); + + final boolean validRelease = !TextUtils.isEmpty(Build.VERSION.RELEASE); + final boolean validId = !TextUtils.isEmpty(Build.ID); + final boolean includeModel = "REL".equals(Build.VERSION.CODENAME) + && !TextUtils.isEmpty(Build.MODEL); + + builder.append("AndroidDownloadManager"); + if (validRelease) { + builder.append("/").append(Build.VERSION.RELEASE); + } + builder.append(" (Linux; U; Android"); + if (validRelease) { + builder.append(" ").append(Build.VERSION.RELEASE); + } + if (includeModel || validId) { + builder.append(";"); + if (includeModel) { + builder.append(" ").append(Build.MODEL); + } + if (validId) { + builder.append(" Build/").append(Build.ID); + } + } + builder.append(")"); + + DEFAULT_USER_AGENT = builder.toString(); + } /** The MIME type of APKs */ public static final String MIMETYPE_APK = "application/vnd.android.package"; diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java index c879e7e0..9ce58cd8 100644 --- a/src/com/android/providers/downloads/DownloadInfo.java +++ b/src/com/android/providers/downloads/DownloadInfo.java @@ -34,6 +34,8 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import com.android.internal.util.IndentingPrintWriter; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; @@ -90,6 +92,7 @@ public class DownloadInfo { 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.mAllowMetered = getInt(Downloads.Impl.COLUMN_ALLOW_METERED) != 0; info.mTitle = getString(Downloads.Impl.COLUMN_TITLE); info.mDescription = getString(Downloads.Impl.COLUMN_DESCRIPTION); info.mBypassRecommendedSizeLimit = @@ -219,6 +222,7 @@ public class DownloadInfo { public boolean mIsPublicApi; public int mAllowedNetworkTypes; public boolean mAllowRoaming; + public boolean mAllowMetered; public String mTitle; public String mDescription; public int mBypassRecommendedSizeLimit; @@ -347,6 +351,9 @@ public class DownloadInfo { if (!isRoamingAllowed() && mSystemFacade.isNetworkRoaming()) { return NETWORK_CANNOT_USE_ROAMING; } + if (!mAllowMetered && mSystemFacade.isActiveNetworkMetered()) { + return NETWORK_TYPE_DISALLOWED_BY_REQUESTOR; + } return checkIsNetworkTypeAllowed(info.getType()); } @@ -477,29 +484,46 @@ public class DownloadInfo { return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, mId); } - public void dump(PrintWriter writer) { - writer.println("DownloadInfo:"); - - writer.print(" mId="); writer.print(mId); - writer.print(" mLastMod="); writer.print(mLastMod); - writer.print(" mPackage="); writer.print(mPackage); - writer.print(" mUid="); writer.println(mUid); - - writer.print(" mUri="); writer.print(mUri); - writer.print(" mMimeType="); writer.print(mMimeType); - writer.print(" mCookies="); writer.print((mCookies != null) ? "yes" : "no"); - writer.print(" mReferer="); writer.println((mReferer != null) ? "yes" : "no"); - - writer.print(" mUserAgent="); writer.println(mUserAgent); - - writer.print(" mFileName="); writer.println(mFileName); - - writer.print(" mStatus="); writer.print(mStatus); - writer.print(" mCurrentBytes="); writer.print(mCurrentBytes); - writer.print(" mTotalBytes="); writer.println(mTotalBytes); - - writer.print(" mNumFailed="); writer.print(mNumFailed); - writer.print(" mRetryAfter="); writer.println(mRetryAfter); + public void dump(IndentingPrintWriter pw) { + pw.println("DownloadInfo:"); + pw.increaseIndent(); + + pw.printPair("mId", mId); + pw.printPair("mLastMod", mLastMod); + pw.printPair("mPackage", mPackage); + pw.printPair("mUid", mUid); + pw.println(); + + pw.printPair("mUri", mUri); + pw.println(); + + pw.printPair("mMimeType", mMimeType); + pw.printPair("mCookies", (mCookies != null) ? "yes" : "no"); + pw.printPair("mReferer", (mReferer != null) ? "yes" : "no"); + pw.printPair("mUserAgent", mUserAgent); + pw.println(); + + pw.printPair("mFileName", mFileName); + pw.printPair("mDestination", mDestination); + pw.println(); + + pw.printPair("mStatus", Downloads.Impl.statusToString(mStatus)); + pw.printPair("mCurrentBytes", mCurrentBytes); + pw.printPair("mTotalBytes", mTotalBytes); + pw.println(); + + pw.printPair("mNumFailed", mNumFailed); + pw.printPair("mRetryAfter", mRetryAfter); + pw.printPair("mETag", mETag); + pw.printPair("mIsPublicApi", mIsPublicApi); + pw.println(); + + pw.printPair("mAllowedNetworkTypes", mAllowedNetworkTypes); + pw.printPair("mAllowRoaming", mAllowRoaming); + pw.printPair("mAllowMetered", mAllowMetered); + pw.println(); + + pw.decreaseIndent(); } /** diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java index 02e5d587..40ebd2bb 100644 --- a/src/com/android/providers/downloads/DownloadProvider.java +++ b/src/com/android/providers/downloads/DownloadProvider.java @@ -38,8 +38,11 @@ import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.Process; import android.provider.Downloads; +import android.provider.OpenableColumns; +import android.text.TextUtils; import android.util.Log; +import com.google.android.collect.Maps; import com.google.common.annotations.VisibleForTesting; import java.io.File; @@ -47,12 +50,12 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; - /** * Allows application to interact with the download manager. */ @@ -60,7 +63,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 = 107; + private static final int DB_VERSION = 108; /** Name of table in the database */ private static final String DB_TABLE = "downloads"; @@ -135,14 +138,24 @@ public final class DownloadProvider extends ContentProvider { Downloads.Impl.COLUMN_FILE_NAME_HINT, Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, Downloads.Impl.COLUMN_DELETED, + OpenableColumns.DISPLAY_NAME, + OpenableColumns.SIZE, }; - private static HashSet<String> sAppReadableColumnsSet; + private static final HashSet<String> sAppReadableColumnsSet; + private static final HashMap<String, String> sColumnsMap; + static { sAppReadableColumnsSet = new HashSet<String>(); for (int i = 0; i < sAppReadableColumnsArray.length; ++i) { sAppReadableColumnsSet.add(sAppReadableColumnsArray[i]); } + + sColumnsMap = Maps.newHashMap(); + sColumnsMap.put(OpenableColumns.DISPLAY_NAME, + Downloads.Impl.COLUMN_TITLE + " AS " + OpenableColumns.DISPLAY_NAME); + sColumnsMap.put(OpenableColumns.SIZE, + Downloads.Impl.COLUMN_TOTAL_BYTES + " AS " + OpenableColumns.SIZE); } private static final List<String> downloadManagerColumnsList = Arrays.asList(DownloadManager.UNDERLYING_COLUMNS); @@ -295,6 +308,11 @@ public final class DownloadProvider extends ContentProvider { addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ERROR_MSG, "TEXT"); break; + case 108: + addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_METERED, + "INTEGER NOT NULL DEFAULT 1"); + break; + default: throw new IllegalStateException("Don't know how to upgrade to " + version); } @@ -440,20 +458,25 @@ public final class DownloadProvider extends ContentProvider { public String getType(final Uri uri) { int match = sURIMatcher.match(uri); switch (match) { - case MY_DOWNLOADS: { + case MY_DOWNLOADS: + case ALL_DOWNLOADS: { return DOWNLOAD_LIST_TYPE; } - case MY_DOWNLOADS_ID: { - return DOWNLOAD_TYPE; - } + case MY_DOWNLOADS_ID: + case ALL_DOWNLOADS_ID: 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, + final String mimeType = DatabaseUtils.stringForQuery(db, "SELECT " + Downloads.Impl.COLUMN_MIME_TYPE + " FROM " + DB_TABLE + " WHERE " + Downloads.Impl._ID + " = ?", new String[]{id}); + if (TextUtils.isEmpty(mimeType)) { + return DOWNLOAD_TYPE; + } else { + return mimeType; + } } default: { if (Constants.LOGV) { @@ -617,6 +640,7 @@ public final class DownloadProvider extends ContentProvider { if (isPublicApi) { copyInteger(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, values, filteredValues); copyBoolean(Downloads.Impl.COLUMN_ALLOW_ROAMING, values, filteredValues); + copyBoolean(Downloads.Impl.COLUMN_ALLOW_METERED, values, filteredValues); } if (Constants.LOGVV) { @@ -752,6 +776,7 @@ public final class DownloadProvider extends ContentProvider { values.remove(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE); // checked later in insert() values.remove(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES); values.remove(Downloads.Impl.COLUMN_ALLOW_ROAMING); + values.remove(Downloads.Impl.COLUMN_ALLOW_METERED); 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(); @@ -827,7 +852,7 @@ public final class DownloadProvider extends ContentProvider { if (shouldRestrictVisibility()) { if (projection == null) { - projection = sAppReadableColumnsArray; + projection = sAppReadableColumnsArray.clone(); } else { // check the validity of the columns in projection for (int i = 0; i < projection.length; ++i) { @@ -838,6 +863,13 @@ public final class DownloadProvider extends ContentProvider { } } } + + for (int i = 0; i < projection.length; i++) { + final String newColumn = sColumnsMap.get(projection[i]); + if (newColumn != null) { + projection[i] = newColumn; + } + } } if (Constants.LOGVV) { @@ -1086,7 +1118,7 @@ public final class DownloadProvider extends ContentProvider { != PackageManager.PERMISSION_GRANTED) { selection.appendClause( Constants.UID + "= ? OR " + Downloads.Impl.COLUMN_OTHER_UID + "= ?", - Binder.getCallingUid(), Binder.getCallingPid()); + Binder.getCallingUid(), Binder.getCallingUid()); } return selection; } diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java index b01384bb..26ad992e 100644 --- a/src/com/android/providers/downloads/DownloadReceiver.java +++ b/src/com/android/providers/downloads/DownloadReceiver.java @@ -61,8 +61,9 @@ public class DownloadReceiver extends BroadcastReceiver { } startService(context); } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - NetworkInfo info = (NetworkInfo) - intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); + final ConnectivityManager connManager = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo info = connManager.getActiveNetworkInfo(); if (info != null && info.isConnected()) { startService(context); } diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java index 5fb46fb9..3b566f8e 100644 --- a/src/com/android/providers/downloads/DownloadService.java +++ b/src/com/android/providers/downloads/DownloadService.java @@ -16,8 +16,7 @@ package com.android.providers.downloads; -import com.google.android.collect.Maps; -import com.google.common.annotations.VisibleForTesting; +import static com.android.providers.downloads.Constants.TAG; import android.app.AlarmManager; import android.app.PendingIntent; @@ -39,15 +38,22 @@ import android.os.RemoteException; import android.provider.Downloads; import android.text.TextUtils; import android.util.Log; +import android.util.Slog; + +import com.android.internal.util.IndentingPrintWriter; +import com.google.android.collect.Maps; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; - /** * Performs the background downloads requested by applications that use the Downloads provider. */ @@ -287,102 +293,104 @@ public class DownloadService extends Service { mPendingUpdate = false; } - long now = mSystemFacade.currentTimeMillis(); - boolean mustScan = false; - keepService = false; - wakeUp = Long.MAX_VALUE; - Set<Long> idsNoLongerInDatabase = new HashSet<Long>(mDownloads.keySet()); - - Cursor cursor = getContentResolver().query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, - null, null, null, null); - if (cursor == null) { - continue; - } - try { - DownloadInfo.Reader reader = - new DownloadInfo.Reader(getContentResolver(), cursor); - int idColumn = cursor.getColumnIndexOrThrow(Downloads.Impl._ID); - if (Constants.LOGVV) { - Log.i(Constants.TAG, "number of rows from downloads-db: " + - cursor.getCount()); + synchronized (mDownloads) { + long now = mSystemFacade.currentTimeMillis(); + boolean mustScan = false; + keepService = false; + wakeUp = Long.MAX_VALUE; + Set<Long> idsNoLongerInDatabase = new HashSet<Long>(mDownloads.keySet()); + + Cursor cursor = getContentResolver().query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, + null, null, null, null); + if (cursor == null) { + continue; } - for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { - long id = cursor.getLong(idColumn); - idsNoLongerInDatabase.remove(id); - DownloadInfo info = mDownloads.get(id); - if (info != null) { - updateDownload(reader, info, now); - } else { - info = insertDownload(reader, now); + try { + DownloadInfo.Reader reader = + new DownloadInfo.Reader(getContentResolver(), cursor); + int idColumn = cursor.getColumnIndexOrThrow(Downloads.Impl._ID); + if (Constants.LOGVV) { + Log.i(Constants.TAG, "number of rows from downloads-db: " + + cursor.getCount()); } + for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { + long id = cursor.getLong(idColumn); + idsNoLongerInDatabase.remove(id); + DownloadInfo info = mDownloads.get(id); + if (info != null) { + updateDownload(reader, info, now); + } else { + info = insertDownloadLocked(reader, now); + } - if (info.shouldScanFile() && !scanFile(info, true, false)) { - mustScan = true; - keepService = true; - } - if (info.hasCompletionNotification()) { - keepService = true; - } - long next = info.nextAction(now); - if (next == 0) { - keepService = true; - } else if (next > 0 && next < wakeUp) { - wakeUp = next; + if (info.shouldScanFile() && !scanFile(info, true, false)) { + mustScan = true; + keepService = true; + } + if (info.hasCompletionNotification()) { + keepService = true; + } + long next = info.nextAction(now); + if (next == 0) { + keepService = true; + } else if (next > 0 && next < wakeUp) { + wakeUp = next; + } } + } finally { + cursor.close(); } - } finally { - cursor.close(); - } - for (Long id : idsNoLongerInDatabase) { - deleteDownload(id); - } + for (Long id : idsNoLongerInDatabase) { + deleteDownloadLocked(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; + // 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(); - } + 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)) { - if (info.shouldScanFile()) { - // initiate rescan of the file to - which will populate - // mediaProviderUri column in this row - if (!scanFile(info, false, true)) { - throw new IllegalStateException("scanFile failed!"); + // 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)) { + if (info.shouldScanFile()) { + // initiate rescan of the file to - which will populate + // mediaProviderUri column in this row + if (!scanFile(info, false, true)) { + throw new IllegalStateException("scanFile failed!"); + } + continue; } - continue; + } else { + // yes it has mediaProviderUri column already filled in. + // delete it from MediaProvider database. + getContentResolver().delete(Uri.parse(info.mMediaProviderUri), null, + null); } - } else { - // yes it has mediaProviderUri column already filled in. - // delete it from MediaProvider database. - getContentResolver().delete(Uri.parse(info.mMediaProviderUri), null, - null); + // 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)}); } - // 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)}); } } } @@ -424,7 +432,7 @@ public class DownloadService extends Service { * Keeps a local copy of the info about a download, and initiates the * download if appropriate. */ - private DownloadInfo insertDownload(DownloadInfo.Reader reader, long now) { + private DownloadInfo insertDownloadLocked(DownloadInfo.Reader reader, long now) { DownloadInfo info = reader.newDownloadInfo(this, mSystemFacade); mDownloads.put(info.mId, info); @@ -466,7 +474,7 @@ public class DownloadService extends Service { /** * Removes the local copy of the info about a download. */ - private void deleteDownload(long id) { + private void deleteDownloadLocked(long id) { DownloadInfo info = mDownloads.get(id); if (info.shouldScanFile()) { scanFile(info, false, false); @@ -475,6 +483,7 @@ public class DownloadService extends Service { info.mStatus = Downloads.Impl.STATUS_CANCELED; } if (info.mDestination != Downloads.Impl.DESTINATION_EXTERNAL && info.mFileName != null) { + Slog.d(TAG, "deleteDownloadLocked() deleting " + info.mFileName); new File(info.mFileName).delete(); } mSystemFacade.cancelNotification(info.mId); @@ -550,7 +559,7 @@ public class DownloadService extends Service { private void deleteFileIfExists(String path) { try { if (!TextUtils.isEmpty(path)) { - Log.i(Constants.TAG, "deleting " + path); + Slog.d(TAG, "deleteFileIfExists() deleting " + path); File file = new File(path); file.delete(); } @@ -561,8 +570,14 @@ public class DownloadService extends Service { @Override protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - for (DownloadInfo info : mDownloads.values()) { - info.dump(writer); + final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); + synchronized (mDownloads) { + final List<Long> ids = Lists.newArrayList(mDownloads.keySet()); + Collections.sort(ids); + for (Long id : ids) { + final DownloadInfo info = mDownloads.get(id); + info.dump(pw); + } } } } diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java index 9080e725..bd91eaa1 100644 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -16,10 +16,11 @@ package com.android.providers.downloads; -import static android.Manifest.permission.MANAGE_NETWORK_POLICY; +import static com.android.providers.downloads.Constants.TAG; import android.content.ContentValues; import android.content.Context; +import android.content.Intent; import android.net.INetworkPolicyListener; import android.net.NetworkPolicyManager; import android.net.Proxy; @@ -32,6 +33,7 @@ import android.provider.Downloads; import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import android.util.Slog; import org.apache.http.Header; import org.apache.http.HttpResponse; @@ -46,7 +48,6 @@ import java.io.InputStream; import java.io.SyncFailedException; import java.net.URI; import java.net.URISyntaxException; -import java.util.Locale; /** * Runs an actual download @@ -74,8 +75,6 @@ public class DownloadThread extends Thread { */ private String userAgent() { String userAgent = mInfo.mUserAgent; - if (userAgent != null) { - } if (userAgent == null) { userAgent = Constants.DEFAULT_USER_AGENT; } @@ -103,7 +102,7 @@ public class DownloadThread extends Thread { public long mTimeLastNotification = 0; public State(DownloadInfo info) { - mMimeType = sanitizeMimeType(info.mMimeType); + mMimeType = Intent.normalizeMimeType(info.mMimeType); mRequestUri = info.mUri; mFilename = info.mFileName; mTotalBytes = info.mTotalBytes; @@ -139,7 +138,7 @@ public class DownloadThread extends Thread { int finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR; String errorMsg = null; - final NetworkPolicyManager netPolicy = NetworkPolicyManager.getSystemService(mContext); + final NetworkPolicyManager netPolicy = NetworkPolicyManager.from(mContext); final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); try { @@ -274,8 +273,6 @@ public class DownloadThread extends Thread { } else if (networkUsable == DownloadInfo.NETWORK_RECOMMENDED_UNUSABLE_DUE_TO_SIZE) { status = Downloads.Impl.STATUS_QUEUED_FOR_WIFI; mInfo.notifyPauseDueToSize(false); - } else if (networkUsable == DownloadInfo.NETWORK_BLOCKED) { - status = Downloads.Impl.STATUS_BLOCKED; } throw new StopRequestException(status, mInfo.getLogMessageForNetworkError(networkUsable)); @@ -333,6 +330,7 @@ public class DownloadThread extends Thread { closeDestination(state); if (state.mFilename != null && Downloads.Impl.isStatusError(finalStatus)) { + Slog.d(TAG, "cleanupDestination() deleting " + state.mFilename); new File(state.mFilename).delete(); state.mFilename = null; } @@ -619,7 +617,7 @@ public class DownloadThread extends Thread { if (state.mMimeType == null) { header = response.getFirstHeader("Content-Type"); if (header != null) { - state.mMimeType = sanitizeMimeType(header.getValue()); + state.mMimeType = Intent.normalizeMimeType(header.getValue()); } } header = response.getFirstHeader("ETag"); @@ -810,8 +808,6 @@ public class DownloadThread extends Thread { case DownloadInfo.NETWORK_UNUSABLE_DUE_TO_SIZE: case DownloadInfo.NETWORK_RECOMMENDED_UNUSABLE_DUE_TO_SIZE: return Downloads.Impl.STATUS_QUEUED_FOR_WIFI; - case DownloadInfo.NETWORK_BLOCKED: - return Downloads.Impl.STATUS_BLOCKED; default: return Downloads.Impl.STATUS_WAITING_FOR_NETWORK; } @@ -851,6 +847,8 @@ public class DownloadThread extends Thread { long fileLength = f.length(); if (fileLength == 0) { // The download hadn't actually started, we can restart from scratch + Slog.d(TAG, "setupDestinationFile() found fileLength=0, deleting " + + state.mFilename); f.delete(); state.mFilename = null; if (Constants.LOGV) { @@ -859,6 +857,8 @@ public class DownloadThread extends Thread { } } else if (mInfo.mETag == null && !mInfo.mNoIntegrity) { // This should've been caught upon failure + Slog.d(TAG, "setupDestinationFile() unable to resume download, deleting " + + state.mFilename); f.delete(); throw new StopRequestException(Downloads.Impl.STATUS_CANNOT_RESUME, "Trying to resume a download that can't be resumed"); @@ -955,33 +955,10 @@ public class DownloadThread extends Thread { mContext.getContentResolver().update(mInfo.getAllDownloadsUri(), values, null, null); } - /** - * Clean up a mimeType string so it can be used to dispatch an intent to - * view a downloaded asset. - * @param mimeType either null or one or more mime types (semi colon separated). - * @return null if mimeType was null. Otherwise a string which represents a - * single mimetype in lowercase and with surrounding whitespaces trimmed. - */ - private static String sanitizeMimeType(String mimeType) { - try { - mimeType = mimeType.trim().toLowerCase(Locale.ENGLISH); - - final int semicolonIndex = mimeType.indexOf(';'); - if (semicolonIndex != -1) { - mimeType = mimeType.substring(0, semicolonIndex); - } - return mimeType; - } catch (NullPointerException npe) { - return null; - } - } - private INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() { @Override public void onUidRulesChanged(int uid, int uidRules) { - // only someone like NPMS should only be calling us - mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, Constants.TAG); - + // caller is NPMS, since we only register with them if (uid == mInfo.mUid) { mPolicyDirty = true; } @@ -989,11 +966,14 @@ public class DownloadThread extends Thread { @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { - // only someone like NPMS should only be calling us - mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, Constants.TAG); + // caller is NPMS, since we only register with them + mPolicyDirty = true; + } + @Override + public void onRestrictBackgroundChanged(boolean restrictBackground) { + // caller is NPMS, since we only register with them mPolicyDirty = true; } }; - } diff --git a/src/com/android/providers/downloads/RealSystemFacade.java b/src/com/android/providers/downloads/RealSystemFacade.java index f2423d47..6580f909 100644 --- a/src/com/android/providers/downloads/RealSystemFacade.java +++ b/src/com/android/providers/downloads/RealSystemFacade.java @@ -42,6 +42,12 @@ class RealSystemFacade implements SystemFacade { return activeInfo; } + @Override + public boolean isActiveNetworkMetered() { + final ConnectivityManager conn = ConnectivityManager.from(mContext); + return conn.isActiveNetworkMetered(); + } + public boolean isNetworkRoaming() { ConnectivityManager connectivity = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/src/com/android/providers/downloads/StorageManager.java b/src/com/android/providers/downloads/StorageManager.java index ab3b6aec..4b51921f 100644 --- a/src/com/android/providers/downloads/StorageManager.java +++ b/src/com/android/providers/downloads/StorageManager.java @@ -16,6 +16,9 @@ package com.android.providers.downloads; +import static com.android.providers.downloads.Constants.LOGV; +import static com.android.providers.downloads.Constants.TAG; + import android.content.ContentUris; import android.content.Context; import android.content.res.Resources; @@ -27,6 +30,7 @@ import android.os.StatFs; import android.provider.Downloads; import android.text.TextUtils; import android.util.Log; +import android.util.Slog; import com.android.internal.R; @@ -35,6 +39,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import libcore.io.ErrnoException; +import libcore.io.Libcore; +import libcore.io.StructStat; + /** * Manages the storage space consumed by Downloads Data dir. When space falls below * a threshold limit (set in resource xml files), starts cleanup of the Downloads data dir @@ -328,10 +336,14 @@ class StorageManager { } long totalFreed = 0; try { + final int dataIndex = cursor.getColumnIndex(Downloads.Impl._DATA); while (cursor.moveToNext() && totalFreed < targetBytes) { - File file = new File(cursor.getString(cursor.getColumnIndex(Downloads.Impl._DATA))); + final String data = cursor.getString(dataIndex); + if (TextUtils.isEmpty(data)) continue; + + File file = new File(data); if (true || Constants.LOGV) { - Log.i(Constants.TAG, "purging " + file.getAbsolutePath() + " for " + + Slog.d(Constants.TAG, "purging " + file.getAbsolutePath() + " for " + file.length() + " bytes"); } totalFreed += file.length(); @@ -383,7 +395,7 @@ class StorageManager { while (cursor.moveToNext()) { String filename = cursor.getString(0); if (!TextUtils.isEmpty(filename)) { - if (true || Constants.LOGV) { + if (LOGV) { Log.i(Constants.TAG, "in removeSpuriousFiles, preserving file " + filename); } @@ -396,16 +408,20 @@ class StorageManager { cursor.close(); } } - // delete the files not found in the database + + // delete files owned by us, but that don't appear in our database + final int myUid = android.os.Process.myUid(); for (File file : files) { - if (file.getName().equals(Constants.KNOWN_SPURIOUS_FILENAME) || - file.getName().equalsIgnoreCase(Constants.RECOVERY_DIRECTORY)) { - continue; - } - if (true || Constants.LOGV) { - Log.i(Constants.TAG, "deleting spurious file " + file.getAbsolutePath()); + final String path = file.getAbsolutePath(); + try { + final StructStat stat = Libcore.os.stat(path); + if (stat.st_uid == myUid) { + Slog.d(TAG, "deleting spurious file " + path); + file.delete(); + } + } catch (ErrnoException e) { + Log.w(TAG, "stat(" + path + ") result: " + e); } - file.delete(); } } diff --git a/src/com/android/providers/downloads/SystemFacade.java b/src/com/android/providers/downloads/SystemFacade.java index a1e4098c..d1439354 100644 --- a/src/com/android/providers/downloads/SystemFacade.java +++ b/src/com/android/providers/downloads/SystemFacade.java @@ -19,6 +19,8 @@ interface SystemFacade { */ public NetworkInfo getActiveNetworkInfo(int uid); + public boolean isActiveNetworkMetered(); + /** * @see android.telephony.TelephonyManager#isNetworkRoaming */ |