diff options
Diffstat (limited to 'src/com')
8 files changed, 319 insertions, 55 deletions
diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java index ef0c6dbb..977f00b9 100644 --- a/src/com/android/providers/downloads/Constants.java +++ b/src/com/android/providers/downloads/Constants.java @@ -91,10 +91,6 @@ public class Constants { /** The default user agent used for downloads */ public static final String DEFAULT_USER_AGENT = "AndroidDownloadManager"; - /** The MIME type of special DRM files */ - public static final String MIMETYPE_DRM_MESSAGE = - android.drm.mobile1.DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING; - /** The MIME type of APKs */ public static final String MIMETYPE_APK = "application/vnd.android.package"; diff --git a/src/com/android/providers/downloads/DownloadDrmHelper.java b/src/com/android/providers/downloads/DownloadDrmHelper.java new file mode 100644 index 00000000..10cb792c --- /dev/null +++ b/src/com/android/providers/downloads/DownloadDrmHelper.java @@ -0,0 +1,110 @@ +/* + * 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.DrmManagerClient; +import android.util.Log; + +public class DownloadDrmHelper { + + /** The MIME type of special DRM files */ + public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message"; + + /** The extensions of special DRM files */ + public static final String EXTENSION_DRM_MESSAGE = ".dm"; + + 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 + * @return True if convert is needed else false + */ + public static boolean isDrmConvertNeeded(String mimetype) { + return MIMETYPE_DRM_MESSAGE.equals(mimetype); + } + + /** + * Modifies the file extension for a DRM Forward Lock file NOTE: This + * function shouldn't be called if the file shouldn't be DRM converted + */ + public static String modifyDrmFwLockFileExtension(String filename) { + if (filename != null) { + int extensionIndex; + extensionIndex = filename.lastIndexOf("."); + if (extensionIndex != -1) { + filename = filename.substring(0, extensionIndex); + } + filename = filename.concat(EXTENSION_INTERNAL_FWDL); + } + return filename; + } + + /** + * 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 + */ + public static String getOriginalMimeType(Context context, String path, String containingMime) { + String result = containingMime; + DrmManagerClient drmClient = new DrmManagerClient(context); + try { + if (drmClient.canHandle(path, null)) { + result = drmClient.getOriginalMimeType(path); + } + } 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."); + } + return result; + } +} diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java index fe6613f7..313386fe 100644 --- a/src/com/android/providers/downloads/DownloadInfo.java +++ b/src/com/android/providers/downloads/DownloadInfo.java @@ -23,7 +23,6 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; -import android.drm.mobile1.DrmRawContent; import android.net.ConnectivityManager; import android.net.Uri; import android.os.Environment; @@ -521,8 +520,7 @@ public class DownloadInfo { && (mDestination == Downloads.Impl.DESTINATION_EXTERNAL || mDestination == Downloads.Impl.DESTINATION_FILE_URI || mDestination == Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) - && Downloads.Impl.isStatusSuccess(mStatus) - && !DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mMimeType); + && Downloads.Impl.isStatusSuccess(mStatus); } void notifyPauseDueToSize(boolean isWifiRequired) { diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java index 7372e4ac..b01384bb 100644 --- a/src/com/android/providers/downloads/DownloadReceiver.java +++ b/src/com/android/providers/downloads/DownloadReceiver.java @@ -150,6 +150,7 @@ public class DownloadReceiver extends BroadcastReceiver { } Intent activityIntent = new Intent(Intent.ACTION_VIEW); + mimetype = DownloadDrmHelper.getOriginalMimeType(context, filename, mimetype); activityIntent.setDataAndType(path, mimetype); activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java index 082caa22..7ddfe959 100644 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -21,7 +21,6 @@ import org.apache.http.conn.params.ConnRouteParams; import android.content.ContentValues; import android.content.Context; import android.content.Intent; -import android.drm.mobile1.DrmRawContent; import android.net.http.AndroidHttpClient; import android.net.Proxy; import android.net.TrafficStats; @@ -29,7 +28,6 @@ import android.os.FileUtils; import android.os.PowerManager; import android.os.Process; import android.provider.Downloads; -import android.provider.DrmStore; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -57,6 +55,7 @@ public class DownloadThread extends Thread { private final DownloadInfo mInfo; private final SystemFacade mSystemFacade; private final StorageManager mStorageManager; + private DrmConvertSession mDrmConvertSession; public DownloadThread(Context context, SystemFacade systemFacade, DownloadInfo info, StorageManager storageManager) { @@ -290,13 +289,9 @@ public class DownloadThread extends Thread { * Called after a successful completion to take any necessary action on the downloaded file. */ private void finalizeDestinationFile(State state) throws StopRequestException { - if (isDrmFile(state)) { - transferToDrm(state); - } else { - // make sure the file is readable - FileUtils.setPermissions(state.mFilename, 0644, -1, -1); - syncDestination(state); - } + // make sure the file is readable + FileUtils.setPermissions(state.mFilename, 0644, -1, -1); + syncDestination(state); } /** @@ -304,6 +299,10 @@ 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)) { new File(state.mFilename).delete(); @@ -341,30 +340,6 @@ public class DownloadThread extends Thread { } /** - * @return true if the current download is a DRM file - */ - private boolean isDrmFile(State state) { - return DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(state.mMimeType); - } - - /** - * Transfer the downloaded destination file to the DRM store. - */ - private void transferToDrm(State state) throws StopRequestException { - File file = new File(state.mFilename); - Intent item = DrmStore.addDrmFile(mContext.getContentResolver(), file, null); - file.delete(); - - if (item == null) { - throw new StopRequestException(Downloads.Impl.STATUS_UNKNOWN_ERROR, - "unable to add file to DrmProvider"); - } else { - state.mFilename = item.getDataString(); - state.mMimeType = item.getType(); - } - } - - /** * Close the destination output stream. */ private void closeDestination(State state) { @@ -429,10 +404,16 @@ public class DownloadThread extends Thread { } mStorageManager.verifySpaceBeforeWritingToFile(mInfo.mDestination, state.mFilename, bytesRead); - state.mStream.write(data, 0, bytesRead); - if (mInfo.mDestination == Downloads.Impl.DESTINATION_EXTERNAL - && !isDrmFile(state)) { - closeDestination(state); + 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."); + } } return; } catch (IOException ex) { @@ -442,6 +423,10 @@ public class DownloadThread extends Thread { if (state.mStream != null) { mStorageManager.verifySpace(mInfo.mDestination, state.mFilename, bytesRead); } + } finally { + if (mInfo.mDestination == Downloads.Impl.DESTINATION_EXTERNAL) { + closeDestination(state); + } } } } @@ -536,6 +521,13 @@ public class DownloadThread extends Thread { } readResponseHeaders(state, innerState, response); + 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, @@ -861,8 +853,7 @@ public class DownloadThread extends Thread { } } - if (state.mStream != null && mInfo.mDestination == Downloads.Impl.DESTINATION_EXTERNAL - && !isDrmFile(state)) { + 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 new file mode 100644 index 00000000..d10edf14 --- /dev/null +++ b/src/com/android/providers/downloads/DrmConvertSession.java @@ -0,0 +1,171 @@ +/* + * 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 1ac77d51..543b652d 100644 --- a/src/com/android/providers/downloads/Helpers.java +++ b/src/com/android/providers/downloads/Helpers.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.drm.mobile1.DrmRawContent; import android.net.Uri; import android.os.Environment; import android.os.SystemClock; @@ -90,7 +89,11 @@ public class Helpers { destination); } storageManager.verifySpace(destination, path, contentLength); - return getFullPath(path, mimeType, destination, base); + path = getFullPath(path, mimeType, destination, base); + if (DownloadDrmHelper.isDrmConvertNeeded(mimeType)) { + path = DownloadDrmHelper.modifyDrmFwLockFileExtension(path); + } + return path; } static String getFullPath(String filename, String mimeType, int destination, @@ -131,7 +134,7 @@ public class Helpers { throw new StopRequestException(Downloads.Impl.STATUS_NOT_ACCEPTABLE, "external download with no mime type not allowed"); } - if (!DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mimeType)) { + 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. diff --git a/src/com/android/providers/downloads/StorageManager.java b/src/com/android/providers/downloads/StorageManager.java index ed241794..228f6681 100644 --- a/src/com/android/providers/downloads/StorageManager.java +++ b/src/com/android/providers/downloads/StorageManager.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.res.Resources; import android.database.Cursor; import android.database.sqlite.SQLiteException; -import android.drm.mobile1.DrmRawContent; import android.net.Uri; import android.os.Environment; import android.os.StatFs; @@ -293,11 +292,6 @@ class StorageManager { } return base; default: - // DRM messages should be temporarily stored internally and then passed to - // the DRM content provider - if (DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mimeType)) { - return mDownloadDataDir; - } throw new IllegalStateException("unexpected value for destination: " + destination); } } |