diff options
author | Jeff Sharkey <jsharkey@android.com> | 2012-12-13 15:43:09 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-12-13 15:43:10 -0800 |
commit | 3b3bbfe2cdd1988a38bcf780191e5273f9a62024 (patch) | |
tree | f8cfceae08c73a6291b18979bcdcbe8ce8578445 /src | |
parent | 226c76b54ed0c1ad1b8a858abbe94551e47476c3 (diff) | |
parent | 5cff4ecb10e89e4fb39cd9e39b8753a31efbe3cc (diff) | |
download | android_packages_providers_DownloadProvider-3b3bbfe2cdd1988a38bcf780191e5273f9a62024.tar.gz android_packages_providers_DownloadProvider-3b3bbfe2cdd1988a38bcf780191e5273f9a62024.tar.bz2 android_packages_providers_DownloadProvider-3b3bbfe2cdd1988a38bcf780191e5273f9a62024.zip |
Merge "Cleaner I/O."
Diffstat (limited to 'src')
5 files changed, 86 insertions, 351 deletions
diff --git a/src/com/android/providers/downloads/DownloadDrmHelper.java b/src/com/android/providers/downloads/DownloadDrmHelper.java index 10cb792c..d1358246 100644 --- a/src/com/android/providers/downloads/DownloadDrmHelper.java +++ b/src/com/android/providers/downloads/DownloadDrmHelper.java @@ -19,7 +19,8 @@ package com.android.providers.downloads; import android.content.Context; import android.drm.DrmManagerClient; -import android.util.Log; + +import java.io.File; public class DownloadDrmHelper { @@ -32,31 +33,6 @@ public class DownloadDrmHelper { public static final String EXTENSION_INTERNAL_FWDL = ".fl"; /** - * Checks if the Media Type is a DRM Media Type - * - * @param drmManagerClient A DrmManagerClient - * @param mimetype Media Type to check - * @return True if the Media Type is DRM else false - */ - public static boolean isDrmMimeType(Context context, String mimetype) { - boolean result = false; - if (context != null) { - try { - DrmManagerClient drmClient = new DrmManagerClient(context); - if (drmClient != null && mimetype != null && mimetype.length() > 0) { - result = drmClient.canHandle("", mimetype); - } - } catch (IllegalArgumentException e) { - Log.w(Constants.TAG, - "DrmManagerClient instance could not be created, context is Illegal."); - } catch (IllegalStateException e) { - Log.w(Constants.TAG, "DrmManagerClient didn't initialize properly."); - } - } - return result; - } - - /** * Checks if the Media Type needs to be DRM converted * * @param mimetype Media type of the content @@ -83,28 +59,20 @@ public class DownloadDrmHelper { } /** - * Gets the original mime type of DRM protected content. - * - * @param context The context - * @param path Path to the file - * @param containingMime The current mime type of of the file i.e. the - * containing mime type - * @return The original mime type of the file if DRM protected else the - * currentMime + * Return the original MIME type of the given file, using the DRM framework + * if the file is protected content. */ - public static String getOriginalMimeType(Context context, String path, String containingMime) { - String result = containingMime; - DrmManagerClient drmClient = new DrmManagerClient(context); + public static String getOriginalMimeType(Context context, File file, String currentMime) { + final DrmManagerClient client = new DrmManagerClient(context); try { - if (drmClient.canHandle(path, null)) { - result = drmClient.getOriginalMimeType(path); + final String rawFile = file.toString(); + if (client.canHandle(rawFile, null)) { + return client.getOriginalMimeType(rawFile); + } else { + return currentMime; } - } catch (IllegalArgumentException ex) { - Log.w(Constants.TAG, - "Can't get original mime type since path is null or empty string."); - } catch (IllegalStateException ex) { - Log.w(Constants.TAG, "DrmManagerClient didn't initialize properly."); + } finally { + client.release(); } - return result; } } diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java index cab1a3e6..2abadb0b 100644 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -25,6 +25,8 @@ import static java.net.HttpURLConnection.HTTP_UNAVAILABLE; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.drm.DrmManagerClient; +import android.drm.DrmOutputStream; import android.net.INetworkPolicyListener; import android.net.NetworkPolicyManager; import android.net.TrafficStats; @@ -42,9 +44,8 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.SyncFailedException; -import java.net.CookieHandler; -import java.net.CookieManager; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; @@ -65,7 +66,6 @@ public class DownloadThread extends Thread { private final DownloadInfo mInfo; private final SystemFacade mSystemFacade; private final StorageManager mStorageManager; - private DrmConvertSession mDrmConvertSession; private volatile boolean mPolicyDirty; @@ -93,7 +93,6 @@ public class DownloadThread extends Thread { */ static class State { public String mFilename; - public FileOutputStream mStream; public String mMimeType; public boolean mCountRetry = false; public int mRetryAfter = 0; @@ -236,10 +235,8 @@ public class DownloadThread extends Thread { * Fully execute a single download request - setup and send the request, handle the response, * and transfer the data to the destination file. */ - private void executeDownload(State state, HttpURLConnection conn) - throws IOException, StopRequestException { + private void executeDownload(State state, HttpURLConnection conn) throws StopRequestException { final InnerState innerState = new InnerState(); - final byte data[] = new byte[Constants.BUFFER_SIZE]; setupDestinationFile(state, innerState); addRequestHeaders(state, conn); @@ -254,14 +251,44 @@ public class DownloadThread extends Thread { // check just before sending the request to avoid using an invalid connection at all checkConnectivity(); - // Asking for response code will execute the request - final int statusCode = conn.getResponseCode(); + InputStream in = null; + OutputStream out = null; + DrmManagerClient drmClient = null; + try { + try { + // Asking for response code will execute the request + final int statusCode = conn.getResponseCode(); + in = conn.getInputStream(); + + handleExceptionalStatus(state, innerState, conn, statusCode); + processResponseHeaders(state, innerState, conn); + } catch (IOException e) { + throw new StopRequestException( + Downloads.Impl.STATUS_HTTP_DATA_ERROR, "Request failed: " + e, e); + } - handleExceptionalStatus(state, innerState, conn, statusCode); - processResponseHeaders(state, innerState, conn); + try { + if (DownloadDrmHelper.isDrmConvertNeeded(state.mMimeType)) { + drmClient = new DrmManagerClient(mContext); + out = new DrmOutputStream( + drmClient, new File(state.mFilename), state.mMimeType); + } else { + out = new FileOutputStream(state.mFilename); + } + } catch (IOException e) { + throw new StopRequestException( + Downloads.Impl.STATUS_FILE_ERROR, "Failed to open destination: " + e, e); + } - final InputStream in = conn.getInputStream(); - transferData(state, innerState, data, in); + transferData(state, innerState, in, out); + + } finally { + if (drmClient != null) { + drmClient.release(); + } + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(out); + } } /** @@ -287,22 +314,21 @@ public class DownloadThread extends Thread { } /** - * Transfer as much data as possible from the HTTP response to the destination file. - * @param data buffer to use to read data - * @param entityStream stream for reading the HTTP response entity + * Transfer as much data as possible from the HTTP response to the + * destination file. */ - private void transferData( - State state, InnerState innerState, byte[] data, InputStream entityStream) + private void transferData(State state, InnerState innerState, InputStream in, OutputStream out) throws StopRequestException { + final byte data[] = new byte[Constants.BUFFER_SIZE]; for (;;) { - int bytesRead = readFromResponse(state, innerState, data, entityStream); + int bytesRead = readFromResponse(state, innerState, data, in); if (bytesRead == -1) { // success, end of stream already reached handleEndOfStream(state, innerState); return; } state.mGotData = true; - writeDataToDestination(state, data, bytesRead); + writeDataToDestination(state, data, bytesRead, out); state.mCurrentBytes += bytesRead; reportProgress(state, innerState); @@ -331,11 +357,6 @@ public class DownloadThread extends Thread { * the downloaded file. */ private void cleanupDestination(State state, int finalStatus) { - if (mDrmConvertSession != null) { - finalStatus = mDrmConvertSession.close(state.mFilename); - } - - closeDestination(state); if (state.mFilename != null && Downloads.Impl.isStatusError(finalStatus)) { if (Constants.LOGVV) { Log.d(TAG, "cleanupDestination() deleting " + state.mFilename); @@ -367,14 +388,6 @@ public class DownloadThread extends Thread { } /** - * Close the destination output stream. - */ - private void closeDestination(State state) { - IoUtils.closeQuietly(state.mStream); - state.mStream = null; - } - - /** * Check if the download has been paused or canceled, stopping the request appropriately if it * has been. */ @@ -433,37 +446,25 @@ public class DownloadThread extends Thread { * @param data buffer containing the data to write * @param bytesRead how many bytes to write from the buffer */ - private void writeDataToDestination(State state, byte[] data, int bytesRead) + private void writeDataToDestination(State state, byte[] data, int bytesRead, OutputStream out) throws StopRequestException { - for (;;) { + mStorageManager.verifySpaceBeforeWritingToFile( + mInfo.mDestination, state.mFilename, bytesRead); + + boolean forceVerified = false; + while (true) { try { - if (state.mStream == null) { - state.mStream = new FileOutputStream(state.mFilename, true); - } - mStorageManager.verifySpaceBeforeWritingToFile(mInfo.mDestination, state.mFilename, - bytesRead); - if (!DownloadDrmHelper.isDrmConvertNeeded(mInfo.mMimeType)) { - state.mStream.write(data, 0, bytesRead); - } else { - byte[] convertedData = mDrmConvertSession.convert(data, bytesRead); - if (convertedData != null) { - state.mStream.write(convertedData, 0, convertedData.length); - } else { - throw new StopRequestException(Downloads.Impl.STATUS_FILE_ERROR, - "Error converting drm data."); - } - } + out.write(data, 0, bytesRead); return; } catch (IOException ex) { - // couldn't write to file. are we out of space? check. - // TODO this check should only be done once. why is this being done - // in a while(true) loop (see the enclosing statement: for(;;) - if (state.mStream != null) { + // TODO: better differentiate between DRM and disk failures + if (!forceVerified) { + // couldn't write to file. are we out of space? check. mStorageManager.verifySpace(mInfo.mDestination, state.mFilename, bytesRead); - } - } finally { - if (mInfo.mDestination == Downloads.Impl.DESTINATION_EXTERNAL) { - closeDestination(state); + forceVerified = true; + } else { + throw new StopRequestException(Downloads.Impl.STATUS_FILE_ERROR, + "Failed to write data: " + ex); } } } @@ -486,7 +487,7 @@ public class DownloadThread extends Thread { if (lengthMismatched) { if (cannotResume(state)) { throw new StopRequestException(Downloads.Impl.STATUS_CANNOT_RESUME, - "mismatched content length"); + "mismatched content length; unable to resume"); } else { throw new StopRequestException(getFinalStatusForHttpError(state), "closed socket before end of file"); @@ -495,7 +496,8 @@ public class DownloadThread extends Thread { } private boolean cannotResume(State state) { - return state.mCurrentBytes > 0 && !mInfo.mNoIntegrity && state.mHeaderETag == null; + return (state.mCurrentBytes > 0 && !mInfo.mNoIntegrity && state.mHeaderETag == null) + || DownloadDrmHelper.isDrmConvertNeeded(state.mMimeType); } /** @@ -513,13 +515,11 @@ public class DownloadThread extends Thread { values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, state.mCurrentBytes); mContext.getContentResolver().update(mInfo.getAllDownloadsUri(), values, null, null); if (cannotResume(state)) { - String message = "while reading response: " + ex.toString() - + ", can't resume interrupted download with no ETag"; throw new StopRequestException(Downloads.Impl.STATUS_CANNOT_RESUME, - message, ex); + "Failed reading response: " + ex + "; unable to resume", ex); } else { throw new StopRequestException(getFinalStatusForHttpError(state), - "while reading response: " + ex.toString(), ex); + "Failed reading response: " + ex, ex); } } } @@ -536,13 +536,6 @@ public class DownloadThread extends Thread { } readResponseHeaders(state, innerState, conn); - if (DownloadDrmHelper.isDrmConvertNeeded(state.mMimeType)) { - mDrmConvertSession = DrmConvertSession.open(mContext, state.mMimeType); - if (mDrmConvertSession == null) { - throw new StopRequestException(Downloads.Impl.STATUS_NOT_ACCEPTABLE, "Mimetype " - + state.mMimeType + " can not be converted."); - } - } state.mFilename = Helpers.generateSaveFile( mContext, @@ -554,15 +547,6 @@ public class DownloadThread extends Thread { mInfo.mDestination, innerState.mContentLength, mInfo.mIsPublicApi, mStorageManager); - try { - state.mStream = new FileOutputStream(state.mFilename); - } catch (FileNotFoundException exc) { - throw new StopRequestException(Downloads.Impl.STATUS_FILE_ERROR, - "while opening destination file: " + exc.toString(), exc); - } - if (Constants.LOGV) { - Log.v(Constants.TAG, "writing " + mInfo.mUri + " to " + state.mFilename); - } updateDatabaseFromHeaders(state, innerState); // check connectivity again now that we know the total size @@ -757,12 +741,6 @@ public class DownloadThread extends Thread { Log.i(Constants.TAG, "resuming download for id: " + mInfo.mId + ", and starting with file of length: " + fileLength); } - try { - state.mStream = new FileOutputStream(state.mFilename, true); - } catch (FileNotFoundException exc) { - throw new StopRequestException(Downloads.Impl.STATUS_FILE_ERROR, - "while opening destination for resuming: " + exc.toString(), exc); - } state.mCurrentBytes = (int) fileLength; if (mInfo.mTotalBytes != -1) { innerState.mContentLength = mInfo.mTotalBytes; @@ -777,10 +755,6 @@ public class DownloadThread extends Thread { } } } - - if (state.mStream != null && mInfo.mDestination == Downloads.Impl.DESTINATION_EXTERNAL) { - closeDestination(state); - } } /** diff --git a/src/com/android/providers/downloads/DrmConvertSession.java b/src/com/android/providers/downloads/DrmConvertSession.java deleted file mode 100644 index d10edf14..00000000 --- a/src/com/android/providers/downloads/DrmConvertSession.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.android.providers.downloads; - -import android.content.Context; -import android.drm.DrmConvertedStatus; -import android.drm.DrmManagerClient; -import android.util.Log; -import android.provider.Downloads; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; - - -public class DrmConvertSession { - private DrmManagerClient mDrmClient; - private int mConvertSessionId; - - private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) { - mDrmClient = drmClient; - mConvertSessionId = convertSessionId; - } - - /** - * Start of converting a file. - * - * @param context The context of the application running the convert session. - * @param mimeType Mimetype of content that shall be converted. - * @return A convert session or null in case an error occurs. - */ - public static DrmConvertSession open(Context context, String mimeType) { - DrmManagerClient drmClient = null; - int convertSessionId = -1; - if (context != null && mimeType != null && !mimeType.equals("")) { - try { - drmClient = new DrmManagerClient(context); - try { - convertSessionId = drmClient.openConvertSession(mimeType); - } catch (IllegalArgumentException e) { - Log.w(Constants.TAG, "Conversion of Mimetype: " + mimeType - + " is not supported.", e); - } catch (IllegalStateException e) { - Log.w(Constants.TAG, "Could not access Open DrmFramework.", e); - } - } catch (IllegalArgumentException e) { - Log.w(Constants.TAG, - "DrmManagerClient instance could not be created, context is Illegal."); - } catch (IllegalStateException e) { - Log.w(Constants.TAG, "DrmManagerClient didn't initialize properly."); - } - } - - if (drmClient == null || convertSessionId < 0) { - return null; - } else { - return new DrmConvertSession(drmClient, convertSessionId); - } - } - /** - * Convert a buffer of data to protected format. - * - * @param buffer Buffer filled with data to convert. - * @param size The number of bytes that shall be converted. - * @return A Buffer filled with converted data, if execution is ok, in all - * other case null. - */ - public byte [] convert(byte[] inBuffer, int size) { - byte[] result = null; - if (inBuffer != null) { - DrmConvertedStatus convertedStatus = null; - try { - if (size != inBuffer.length) { - byte[] buf = new byte[size]; - System.arraycopy(inBuffer, 0, buf, 0, size); - convertedStatus = mDrmClient.convertData(mConvertSessionId, buf); - } else { - convertedStatus = mDrmClient.convertData(mConvertSessionId, inBuffer); - } - - if (convertedStatus != null && - convertedStatus.statusCode == DrmConvertedStatus.STATUS_OK && - convertedStatus.convertedData != null) { - result = convertedStatus.convertedData; - } - } catch (IllegalArgumentException e) { - Log.w(Constants.TAG, "Buffer with data to convert is illegal. Convertsession: " - + mConvertSessionId, e); - } catch (IllegalStateException e) { - Log.w(Constants.TAG, "Could not convert data. Convertsession: " + - mConvertSessionId, e); - } - } else { - throw new IllegalArgumentException("Parameter inBuffer is null"); - } - return result; - } - - /** - * Ends a conversion session of a file. - * - * @param fileName The filename of the converted file. - * @return Downloads.Impl.STATUS_SUCCESS if execution is ok. - * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not - * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem - * occurs when accessing drm framework. - * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred. - */ - public int close(String filename) { - DrmConvertedStatus convertedStatus = null; - int result = Downloads.Impl.STATUS_UNKNOWN_ERROR; - if (mDrmClient != null && mConvertSessionId >= 0) { - try { - convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId); - if (convertedStatus == null || - convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK || - convertedStatus.convertedData == null) { - result = Downloads.Impl.STATUS_NOT_ACCEPTABLE; - } else { - RandomAccessFile rndAccessFile = null; - try { - rndAccessFile = new RandomAccessFile(filename, "rw"); - rndAccessFile.seek(convertedStatus.offset); - rndAccessFile.write(convertedStatus.convertedData); - result = Downloads.Impl.STATUS_SUCCESS; - } catch (FileNotFoundException e) { - result = Downloads.Impl.STATUS_FILE_ERROR; - Log.w(Constants.TAG, "File: " + filename + " could not be found.", e); - } catch (IOException e) { - result = Downloads.Impl.STATUS_FILE_ERROR; - Log.w(Constants.TAG, "Could not access File: " + filename + " .", e); - } catch (IllegalArgumentException e) { - result = Downloads.Impl.STATUS_FILE_ERROR; - Log.w(Constants.TAG, "Could not open file in mode: rw", e); - } catch (SecurityException e) { - Log.w(Constants.TAG, "Access to File: " + filename + - " was denied denied by SecurityManager.", e); - } finally { - if (rndAccessFile != null) { - try { - rndAccessFile.close(); - } catch (IOException e) { - result = Downloads.Impl.STATUS_FILE_ERROR; - Log.w(Constants.TAG, "Failed to close File:" + filename - + ".", e); - } - } - } - } - } catch (IllegalStateException e) { - Log.w(Constants.TAG, "Could not close convertsession. Convertsession: " + - mConvertSessionId, e); - } - } - return result; - } -} diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java index 484c9256..225b8d49 100644 --- a/src/com/android/providers/downloads/Helpers.java +++ b/src/com/android/providers/downloads/Helpers.java @@ -81,7 +81,6 @@ public class Helpers { if (contentLength < 0) { contentLength = 0; } - checkCanHandleDownload(context, mimeType, destination, isPublicApi); String path; File base = null; if (destination == Downloads.Impl.DESTINATION_FILE_URI) { @@ -136,47 +135,6 @@ public class Helpers { return chooseUniqueFilename(destination, filename, extension, recoveryDir); } - private static void checkCanHandleDownload(Context context, String mimeType, int destination, - boolean isPublicApi) throws StopRequestException { - if (isPublicApi) { - return; - } - - if (destination == Downloads.Impl.DESTINATION_EXTERNAL - || destination == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE) { - if (mimeType == null) { - throw new StopRequestException(Downloads.Impl.STATUS_NOT_ACCEPTABLE, - "external download with no mime type not allowed"); - } - if (!DownloadDrmHelper.isDrmMimeType(context, mimeType)) { - // Check to see if we are allowed to download this file. Only files - // that can be handled by the platform can be downloaded. - // special case DRM files, which we should always allow downloading. - Intent intent = new Intent(Intent.ACTION_VIEW); - - // We can provide data as either content: or file: URIs, - // so allow both. (I think it would be nice if we just did - // everything as content: URIs) - // Actually, right now the download manager's UId restrictions - // prevent use from using content: so it's got to be file: or - // nothing - - PackageManager pm = context.getPackageManager(); - intent.setDataAndType(Uri.fromParts("file", "", null), mimeType); - ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); - //Log.i(Constants.TAG, "*** FILENAME QUERY " + intent + ": " + list); - - if (ri == null) { - if (Constants.LOGV) { - Log.v(Constants.TAG, "no handler found for type " + mimeType); - } - throw new StopRequestException(Downloads.Impl.STATUS_NOT_ACCEPTABLE, - "no handler found for this download type"); - } - } - } - } - private static String chooseFilename(String url, String hint, String contentDisposition, String contentLocation, int destination) { String filename = null; diff --git a/src/com/android/providers/downloads/OpenHelper.java b/src/com/android/providers/downloads/OpenHelper.java index 7eca95c9..0d5f5e92 100644 --- a/src/com/android/providers/downloads/OpenHelper.java +++ b/src/com/android/providers/downloads/OpenHelper.java @@ -30,6 +30,8 @@ import android.database.Cursor; import android.net.Uri; import android.provider.Downloads.Impl.RequestHeaders; +import java.io.File; + public class OpenHelper { /** * Build an {@link Intent} to view the download at current {@link Cursor} @@ -47,9 +49,9 @@ public class OpenHelper { } final Uri localUri = getCursorUri(cursor, COLUMN_LOCAL_URI); - final String filename = getCursorString(cursor, COLUMN_LOCAL_FILENAME); + final File file = getCursorFile(cursor, COLUMN_LOCAL_FILENAME); String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE); - mimeType = DownloadDrmHelper.getOriginalMimeType(context, filename, mimeType); + mimeType = DownloadDrmHelper.getOriginalMimeType(context, file, mimeType); final Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); @@ -122,4 +124,8 @@ public class OpenHelper { private static long getCursorLong(Cursor cursor, String column) { return cursor.getLong(cursor.getColumnIndexOrThrow(column)); } + + private static File getCursorFile(Cursor cursor, String column) { + return new File(cursor.getString(cursor.getColumnIndexOrThrow(column))); + } } |