summaryrefslogtreecommitdiffstats
path: root/src/com/android/providers
diff options
context:
space:
mode:
authorRicardo Cerqueira <ricardo@cyngn.com>2015-03-10 12:28:19 +0000
committerRicardo Cerqueira <ricardo@cyngn.com>2015-03-10 12:28:19 +0000
commit1958bec7cd1cd58a0ee83fcd837ff19c226d6470 (patch)
treee6bfd4d91dcfeb5d9dd1ce9adccafa3ac4574145 /src/com/android/providers
parent05775ecc734a8fa1de16692962b5ee2808332b7e (diff)
parent1de359e31359f9aa6c500b378082e9d6e37d94b3 (diff)
downloadandroid_packages_providers_DownloadProvider-1958bec7cd1cd58a0ee83fcd837ff19c226d6470.tar.gz
android_packages_providers_DownloadProvider-1958bec7cd1cd58a0ee83fcd837ff19c226d6470.tar.bz2
android_packages_providers_DownloadProvider-1958bec7cd1cd58a0ee83fcd837ff19c226d6470.zip
Merge tag 'android-5.1.0_r1' into HEADstaging/cm-12.1
Android 5.1.0 release 1
Diffstat (limited to 'src/com/android/providers')
-rw-r--r--src/com/android/providers/downloads/DownloadNotifier.java6
-rw-r--r--src/com/android/providers/downloads/DownloadStorageProvider.java4
-rw-r--r--src/com/android/providers/downloads/DownloadThread.java52
-rw-r--r--src/com/android/providers/downloads/Helpers.java51
4 files changed, 47 insertions, 66 deletions
diff --git a/src/com/android/providers/downloads/DownloadNotifier.java b/src/com/android/providers/downloads/DownloadNotifier.java
index 0fb5a583..3af97463 100644
--- a/src/com/android/providers/downloads/DownloadNotifier.java
+++ b/src/com/android/providers/downloads/DownloadNotifier.java
@@ -43,6 +43,7 @@ import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
+import java.text.NumberFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
@@ -221,8 +222,8 @@ public class DownloadNotifier {
}
if (total > 0) {
- final int percent = (int) ((current * 100) / total);
- percentText = res.getString(R.string.download_percent, percent);
+ percentText =
+ NumberFormat.getPercentInstance().format((double) current / total);
if (speed > 0) {
final long remainingMillis = ((total - current) * 1000) / speed;
@@ -248,6 +249,7 @@ public class DownloadNotifier {
speedAsSizeText = Formatter.formatFileSize(mContext, speed);
}
+ final int percent = (int) ((current * 100) / total);
builder.setProgress(100, percent, false);
} else {
builder.setProgress(100, 0, true);
diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java
index 78b3c430..80d78551 100644
--- a/src/com/android/providers/downloads/DownloadStorageProvider.java
+++ b/src/com/android/providers/downloads/DownloadStorageProvider.java
@@ -42,6 +42,7 @@ import libcore.io.IoUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.text.NumberFormat;
/**
* Presents a {@link DocumentsContract} view of {@link DownloadManager}
@@ -321,7 +322,8 @@ public class DownloadStorageProvider extends DocumentsProvider {
final long progress = cursor.getLong(cursor.getColumnIndexOrThrow(
DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
if (size != null) {
- final long percent = progress * 100 / size;
+ String percent =
+ NumberFormat.getPercentInstance().format((double) progress / size);
summary = getContext().getString(R.string.download_running_percent, percent);
} else {
summary = getContext().getString(R.string.download_running);
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index aa0190bf..c75e4193 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -17,6 +17,7 @@
package com.android.providers.downloads;
import static android.provider.Downloads.Impl.STATUS_BAD_REQUEST;
+import static android.provider.Downloads.Impl.STATUS_CANCELED;
import static android.provider.Downloads.Impl.STATUS_CANNOT_RESUME;
import static android.provider.Downloads.Impl.STATUS_FILE_ERROR;
import static android.provider.Downloads.Impl.STATUS_HTTP_DATA_ERROR;
@@ -143,10 +144,7 @@ public class DownloadThread implements Runnable {
mETag = info.mETag;
}
- /**
- * Push update of current delta values to provider.
- */
- public void writeToDatabase() {
+ private ContentValues buildContentValues() {
final ContentValues values = new ContentValues();
values.put(Downloads.Impl.COLUMN_URI, mUri);
@@ -162,7 +160,26 @@ public class DownloadThread implements Runnable {
values.put(Downloads.Impl.COLUMN_LAST_MODIFICATION, mSystemFacade.currentTimeMillis());
values.put(Downloads.Impl.COLUMN_ERROR_MSG, mErrorMsg);
- mContext.getContentResolver().update(mInfo.getAllDownloadsUri(), values, null, null);
+ return values;
+ }
+
+ /**
+ * Blindly push update of current delta values to provider.
+ */
+ public void writeToDatabase() {
+ mContext.getContentResolver().update(mInfo.getAllDownloadsUri(), buildContentValues(),
+ null, null);
+ }
+
+ /**
+ * Push update of current delta values to provider, asserting strongly
+ * that we haven't been paused or deleted.
+ */
+ public void writeToDatabaseOrThrow() throws StopRequestException {
+ if (mContext.getContentResolver().update(mInfo.getAllDownloadsUri(),
+ buildContentValues(), Downloads.Impl.COLUMN_DELETED + " == '0'", null) == 0) {
+ throw new StopRequestException(STATUS_CANCELED, "Download deleted or missing!");
+ }
}
}
@@ -422,12 +439,16 @@ public class DownloadThread implements Runnable {
*/
private void transferData(HttpURLConnection conn) throws StopRequestException {
- // To detect when we're really finished, we either need a length or
- // chunked encoding.
+ // To detect when we're really finished, we either need a length, closed
+ // connection, or chunked encoding.
final boolean hasLength = mInfoDelta.mTotalBytes != -1;
- final String transferEncoding = conn.getHeaderField("Transfer-Encoding");
- final boolean isChunked = "chunked".equalsIgnoreCase(transferEncoding);
- if (!hasLength && !isChunked) {
+ final boolean isConnectionClose = "close".equalsIgnoreCase(
+ conn.getHeaderField("Connection"));
+ final boolean isEncodingChunked = "chunked".equalsIgnoreCase(
+ conn.getHeaderField("Transfer-Encoding"));
+
+ final boolean finishKnown = hasLength || isConnectionClose || isEncodingChunked;
+ if (!finishKnown) {
throw new StopRequestException(
STATUS_CANNOT_RESUME, "can't know size of download, giving up");
}
@@ -666,7 +687,7 @@ public class DownloadThread implements Runnable {
/**
* Report download progress through the database if necessary.
*/
- private void updateProgress(FileDescriptor outFd) throws IOException {
+ private void updateProgress(FileDescriptor outFd) throws IOException, StopRequestException {
final long now = SystemClock.elapsedRealtime();
final long currentBytes = mInfoDelta.mCurrentBytes;
@@ -697,7 +718,7 @@ public class DownloadThread implements Runnable {
// so we can always resume based on latest database information.
outFd.sync();
- mInfoDelta.writeToDatabase();
+ mInfoDelta.writeToDatabaseOrThrow();
mLastUpdateBytes = currentBytes;
mLastUpdateTime = now;
@@ -736,7 +757,7 @@ public class DownloadThread implements Runnable {
mInfoDelta.mETag = conn.getHeaderField("ETag");
- mInfoDelta.writeToDatabase();
+ mInfoDelta.writeToDatabaseOrThrow();
// Check connectivity again now that we know the total size
checkConnectivity();
@@ -775,6 +796,10 @@ public class DownloadThread implements Runnable {
// easily resume partial downloads.
conn.setRequestProperty("Accept-Encoding", "identity");
+ // Defeat connection reuse, since otherwise servers may continue
+ // streaming large downloads after cancelled.
+ conn.setRequestProperty("Connection", "close");
+
if (resuming) {
if (mInfoDelta.mETag != null) {
conn.addRequestProperty("If-Match", mInfoDelta.mETag);
@@ -834,6 +859,7 @@ public class DownloadThread implements Runnable {
case STATUS_HTTP_DATA_ERROR:
case HTTP_UNAVAILABLE:
case HTTP_INTERNAL_ERROR:
+ case STATUS_FILE_ERROR:
return true;
default:
return false;
diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java
index eb071395..0aa49c0a 100644
--- a/src/com/android/providers/downloads/Helpers.java
+++ b/src/com/android/providers/downloads/Helpers.java
@@ -214,7 +214,7 @@ public class Helpers {
// The VFAT file system is assumed as target for downloads.
// Replace invalid characters according to the specifications of VFAT.
- filename = replaceInvalidVfatCharacters(filename);
+ filename = FileUtils.buildValidFatFilename(filename);
return filename;
}
@@ -665,53 +665,4 @@ public class Helpers {
(c >= '0' && c <= '9');
}
}
-
- /**
- * Replace invalid filename characters according to
- * specifications of the VFAT.
- * @note Package-private due to testing.
- */
- private static String replaceInvalidVfatCharacters(String filename) {
- final char START_CTRLCODE = 0x00;
- final char END_CTRLCODE = 0x1f;
- final char QUOTEDBL = 0x22;
- final char ASTERISK = 0x2A;
- final char SLASH = 0x2F;
- final char COLON = 0x3A;
- final char LESS = 0x3C;
- final char GREATER = 0x3E;
- final char QUESTION = 0x3F;
- final char BACKSLASH = 0x5C;
- final char BAR = 0x7C;
- final char DEL = 0x7F;
- final char UNDERSCORE = 0x5F;
-
- StringBuffer sb = new StringBuffer();
- char ch;
- boolean isRepetition = false;
- for (int i = 0; i < filename.length(); i++) {
- ch = filename.charAt(i);
- if ((START_CTRLCODE <= ch &&
- ch <= END_CTRLCODE) ||
- ch == QUOTEDBL ||
- ch == ASTERISK ||
- ch == SLASH ||
- ch == COLON ||
- ch == LESS ||
- ch == GREATER ||
- ch == QUESTION ||
- ch == BACKSLASH ||
- ch == BAR ||
- ch == DEL){
- if (!isRepetition) {
- sb.append(UNDERSCORE);
- isRepetition = true;
- }
- } else {
- sb.append(ch);
- isRepetition = false;
- }
- }
- return sb.toString();
- }
}