diff options
author | Tom Taylor <tomtaylor@google.com> | 2012-03-27 15:42:16 -0700 |
---|---|---|
committer | Tom Taylor <tomtaylor@google.com> | 2012-03-27 15:42:16 -0700 |
commit | ca184870ca6db8cda264a466582f19e71f7233cd (patch) | |
tree | 08505a7511b280879424818480e47262521b5d28 /core/java/com/google | |
parent | 8b5bce8d9060c1b68b5605da8f6978d8fb5ea1d3 (diff) | |
download | frameworks_base-ca184870ca6db8cda264a466582f19e71f7233cd.zip frameworks_base-ca184870ca6db8cda264a466582f19e71f7233cd.tar.gz frameworks_base-ca184870ca6db8cda264a466582f19e71f7233cd.tar.bz2 |
Drm encode mms parts while persisting the pdu
Bug 5158047
Change-Id: I88aba9a91192fb1eb730b48efad3b2b488abad3d
Diffstat (limited to 'core/java/com/google')
3 files changed, 404 insertions, 3 deletions
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java index b04f890..7c937ed 100644 --- a/core/java/com/google/android/mms/pdu/PduPersister.java +++ b/core/java/com/google/android/mms/pdu/PduPersister.java @@ -20,6 +20,8 @@ package com.google.android.mms.pdu; import com.google.android.mms.ContentType; import com.google.android.mms.InvalidHeaderValueException; import com.google.android.mms.MmsException; +import com.google.android.mms.util.DownloadDrmHelper; +import com.google.android.mms.util.DrmConvertSession; import com.google.android.mms.util.PduCache; import com.google.android.mms.util.PduCacheEntry; import com.google.android.mms.util.SqliteWrapper; @@ -30,7 +32,11 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteException; +import android.drm.DrmManagerClient; import android.net.Uri; +import android.os.FileUtils; +import android.provider.MediaStore; import android.provider.Telephony; import android.provider.Telephony.Mms; import android.provider.Telephony.MmsSms; @@ -42,6 +48,7 @@ import android.text.TextUtils; import android.util.Log; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -271,10 +278,12 @@ public class PduPersister { private final Context mContext; private final ContentResolver mContentResolver; + private final DrmManagerClient mDrmManagerClient; private PduPersister(Context context) { mContext = context; mContentResolver = context.getContentResolver(); + mDrmManagerClient = new DrmManagerClient(context); } /** Get(or create if not exist) an instance of PduPersister */ @@ -761,6 +770,9 @@ public class PduPersister { throws MmsException { OutputStream os = null; InputStream is = null; + DrmConvertSession drmConvertSession = null; + Uri dataUri = null; + String path = null; try { byte[] data = part.getData(); @@ -773,9 +785,38 @@ public class PduPersister { throw new MmsException("unable to update " + uri.toString()); } } else { + boolean isDrm = DownloadDrmHelper.isDrmConvertNeeded(contentType); + if (isDrm) { + if (uri != null) { + try { + path = convertUriToPath(mContext, uri); + if (LOCAL_LOGV) { + Log.v(TAG, "drm uri: " + uri + " path: " + path); + } + File f = new File(path); + long len = f.length(); + if (LOCAL_LOGV) { + Log.v(TAG, "drm path: " + path + " len: " + len); + } + if (len > 0) { + // we're not going to re-persist and re-encrypt an already + // converted drm file + return; + } + } catch (Exception e) { + Log.e(TAG, "Can't get file info for: " + part.getDataUri(), e); + } + } + // We haven't converted the file yet, start the conversion + drmConvertSession = DrmConvertSession.open(mContext, contentType); + if (drmConvertSession == null) { + throw new MmsException("Mimetype " + contentType + + " can not be converted."); + } + } os = mContentResolver.openOutputStream(uri); if (data == null) { - Uri dataUri = part.getDataUri(); + dataUri = part.getDataUri(); if ((dataUri == null) || (dataUri == uri)) { Log.w(TAG, "Can't find data for this part."); return; @@ -788,13 +829,32 @@ public class PduPersister { byte[] buffer = new byte[8192]; for (int len = 0; (len = is.read(buffer)) != -1; ) { - os.write(buffer, 0, len); + if (!isDrm) { + os.write(buffer, 0, len); + } else { + byte[] convertedData = drmConvertSession.convert(buffer, len); + if (convertedData != null) { + os.write(convertedData, 0, convertedData.length); + } else { + throw new MmsException("Error converting drm data."); + } + } } } else { if (LOCAL_LOGV) { Log.v(TAG, "Saving data to: " + uri); } - os.write(data); + if (!isDrm) { + os.write(data); + } else { + dataUri = uri; + byte[] convertedData = drmConvertSession.convert(data, data.length); + if (convertedData != null) { + os.write(convertedData, 0, convertedData.length); + } else { + throw new MmsException("Error converting drm data."); + } + } } } } catch (FileNotFoundException e) { @@ -818,7 +878,65 @@ public class PduPersister { Log.e(TAG, "IOException while closing: " + is, e); } // Ignore } + if (drmConvertSession != null) { + drmConvertSession.close(path); + + // Reset the permissions on the encrypted part file so everyone has only read + // permission. + File f = new File(path); + ContentValues values = new ContentValues(0); + SqliteWrapper.update(mContext, mContentResolver, + Uri.parse("content://mms/resetFilePerm/" + f.getName()), + values, null, null); + } + } + } + + /** + * This method expects uri in the following format + * content://media/<table_name>/<row_index> (or) + * file://sdcard/test.mp4 + * http://test.com/test.mp4 + * + * Here <table_name> shall be "video" or "audio" or "images" + * <row_index> the index of the content in given table + */ + static public String convertUriToPath(Context context, Uri uri) { + String path = null; + if (null != uri) { + String scheme = uri.getScheme(); + if (null == scheme || scheme.equals("") || + scheme.equals(ContentResolver.SCHEME_FILE)) { + path = uri.getPath(); + + } else if (scheme.equals("http")) { + path = uri.toString(); + + } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) { + String[] projection = new String[] {MediaStore.MediaColumns.DATA}; + Cursor cursor = null; + try { + cursor = context.getContentResolver().query(uri, projection, null, + null, null); + if (null == cursor || 0 == cursor.getCount() || !cursor.moveToFirst()) { + throw new IllegalArgumentException("Given Uri could not be found" + + " in media store"); + } + int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA); + path = cursor.getString(pathIndex); + } catch (SQLiteException e) { + throw new IllegalArgumentException("Given Uri is not formatted in a way " + + "so that it can be found in media store."); + } finally { + if (null != cursor) { + cursor.close(); + } + } + } else { + throw new IllegalArgumentException("Given Uri scheme is not supported"); + } } + return path; } private void updateAddress( diff --git a/core/java/com/google/android/mms/util/DownloadDrmHelper.java b/core/java/com/google/android/mms/util/DownloadDrmHelper.java new file mode 100644 index 0000000..6852eca --- /dev/null +++ b/core/java/com/google/android/mms/util/DownloadDrmHelper.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2012 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.google.android.mms.util; + +import android.content.Context; +import android.drm.DrmManagerClient; +import android.util.Log; + +public class DownloadDrmHelper { + private static final String TAG = "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(TAG, + "DrmManagerClient instance could not be created, context is Illegal."); + } catch (IllegalStateException e) { + Log.w(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(TAG, + "Can't get original mime type since path is null or empty string."); + } catch (IllegalStateException ex) { + Log.w(TAG, "DrmManagerClient didn't initialize properly."); + } + return result; + } +} diff --git a/core/java/com/google/android/mms/util/DrmConvertSession.java b/core/java/com/google/android/mms/util/DrmConvertSession.java new file mode 100644 index 0000000..2d8f274 --- /dev/null +++ b/core/java/com/google/android/mms/util/DrmConvertSession.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2012 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.google.android.mms.util; + +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 static final String TAG = "DrmConvertSession"; + + 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(TAG, "Conversion of Mimetype: " + mimeType + + " is not supported.", e); + } catch (IllegalStateException e) { + Log.w(TAG, "Could not access Open DrmFramework.", e); + } + } catch (IllegalArgumentException e) { + Log.w(TAG, + "DrmManagerClient instance could not be created, context is Illegal."); + } catch (IllegalStateException e) { + Log.w(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(TAG, "Buffer with data to convert is illegal. Convertsession: " + + mConvertSessionId, e); + } catch (IllegalStateException e) { + Log.w(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(TAG, "File: " + filename + " could not be found.", e); + } catch (IOException e) { + result = Downloads.Impl.STATUS_FILE_ERROR; + Log.w(TAG, "Could not access File: " + filename + " .", e); + } catch (IllegalArgumentException e) { + result = Downloads.Impl.STATUS_FILE_ERROR; + Log.w(TAG, "Could not open file in mode: rw", e); + } catch (SecurityException e) { + Log.w(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(TAG, "Failed to close File:" + filename + + ".", e); + } + } + } + } + } catch (IllegalStateException e) { + Log.w(TAG, "Could not close convertsession. Convertsession: " + + mConvertSessionId, e); + } + } + return result; + } +} |