Download provider change for DRM Forward Lock plugin:
Gloria Wang [Wed, 16 Feb 2011 19:58:51 +0000 (11:58 -0800)]
to convert .dm files to .fl files during downloading
For bug 3188041

Change-Id: I882b851664432fba3e57dc25a6be827b48006e69

src/com/android/providers/downloads/Constants.java
src/com/android/providers/downloads/DownloadDrmHelper.java [new file with mode: 0644]
src/com/android/providers/downloads/DownloadInfo.java
src/com/android/providers/downloads/DownloadReceiver.java
src/com/android/providers/downloads/DownloadThread.java
src/com/android/providers/downloads/DrmConvertSession.java [new file with mode: 0644]
src/com/android/providers/downloads/Helpers.java
src/com/android/providers/downloads/StorageManager.java

index ef0c6db..977f00b 100644 (file)
@@ -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 (file)
index 0000000..10cb792
--- /dev/null
@@ -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;
+    }
+}
index bd8df86..2f703e6 100644 (file)
@@ -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;
@@ -522,8 +521,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) {
index 7372e4a..b01384b 100644 (file)
@@ -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 {
index fbd3b82..2e3543d 100644 (file)
@@ -21,14 +21,12 @@ 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.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;
@@ -56,6 +54,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) {
@@ -281,13 +280,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);
     }
 
     /**
@@ -295,6 +290,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();
@@ -332,30 +331,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) {
@@ -420,10 +395,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) {
@@ -433,6 +414,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);
+                }
             }
         }
     }
@@ -527,6 +512,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,
@@ -852,8 +844,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 (file)
index 0000000..d10edf1
--- /dev/null
@@ -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;
+    }
+}
index 359738a..ab09551 100644 (file)
@@ -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;
@@ -91,7 +90,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,
@@ -132,7 +135,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.
index ed24179..228f668 100644 (file)
@@ -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);
         }
     }