summaryrefslogtreecommitdiffstats
path: root/src/com/android/providers
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/providers')
-rw-r--r--src/com/android/providers/downloads/Constants.java37
-rw-r--r--src/com/android/providers/downloads/DownloadInfo.java70
-rw-r--r--src/com/android/providers/downloads/DownloadProvider.java52
-rw-r--r--src/com/android/providers/downloads/DownloadReceiver.java5
-rw-r--r--src/com/android/providers/downloads/DownloadService.java197
-rw-r--r--src/com/android/providers/downloads/DownloadThread.java56
-rw-r--r--src/com/android/providers/downloads/RealSystemFacade.java6
-rw-r--r--src/com/android/providers/downloads/StorageManager.java38
-rw-r--r--src/com/android/providers/downloads/SystemFacade.java2
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
*/