diff --git a/src/org/thoughtcrime/securesms/groups/GroupManager.java b/src/org/thoughtcrime/securesms/groups/GroupManager.java index 2b51cffd5..9fee1eda1 100644 --- a/src/org/thoughtcrime/securesms/groups/GroupManager.java +++ b/src/org/thoughtcrime/securesms/groups/GroupManager.java @@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; -import org.thoughtcrime.securesms.providers.SingleUseBlobProvider; +import org.thoughtcrime.securesms.providers.MemoryBlobProvider; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.util.BitmapUtil; @@ -111,7 +111,7 @@ public class GroupManager { GroupContext groupContext = groupContextBuilder.build(); if (avatar != null) { - Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar); + Uri avatarUri = MemoryBlobProvider.getInstance().createSingleUseUri(avatar); avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false); } diff --git a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java index 95a4d22e1..23b38f8ad 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java @@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsRadioException; import org.thoughtcrime.securesms.mms.PartParser; import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.providers.SingleUseBlobProvider; +import org.thoughtcrime.securesms.providers.MemoryBlobProvider; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; @@ -174,7 +174,7 @@ public class MmsDownloadJob extends MasterSecretJob { LegacyMessageException { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - SingleUseBlobProvider provider = SingleUseBlobProvider.getInstance(); + MemoryBlobProvider provider = MemoryBlobProvider.getInstance(); Optional
group = Optional.absent(); Set
members = new HashSet<>(); String body = null; @@ -213,7 +213,7 @@ public class MmsDownloadJob extends MasterSecretJob { PduPart part = media.getPart(i); if (part.getData() != null) { - Uri uri = provider.createUri(part.getData()); + Uri uri = provider.createSingleUseUri(part.getData()); String name = null; if (part.getName() != null) name = Util.toIsoString(part.getName()); diff --git a/src/org/thoughtcrime/securesms/mms/PartAuthority.java b/src/org/thoughtcrime/securesms/mms/PartAuthority.java index 659888f88..78fd2610b 100644 --- a/src/org/thoughtcrime/securesms/mms/PartAuthority.java +++ b/src/org/thoughtcrime/securesms/mms/PartAuthority.java @@ -9,11 +9,10 @@ import android.support.annotation.Nullable; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.AttachmentId; -import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.providers.PersistentBlobProvider; import org.thoughtcrime.securesms.providers.PartProvider; -import org.thoughtcrime.securesms.providers.SingleUseBlobProvider; +import org.thoughtcrime.securesms.providers.MemoryBlobProvider; import java.io.IOException; import java.io.InputStream; @@ -38,7 +37,7 @@ public class PartAuthority { uriMatcher.addURI("org.thoughtcrime.securesms", "thumb/*/#", THUMB_ROW); uriMatcher.addURI(PersistentBlobProvider.AUTHORITY, PersistentBlobProvider.EXPECTED_PATH_OLD, PERSISTENT_ROW); uriMatcher.addURI(PersistentBlobProvider.AUTHORITY, PersistentBlobProvider.EXPECTED_PATH_NEW, PERSISTENT_ROW); - uriMatcher.addURI(SingleUseBlobProvider.AUTHORITY, SingleUseBlobProvider.PATH, SINGLE_USE_ROW); + uriMatcher.addURI(MemoryBlobProvider.AUTHORITY, MemoryBlobProvider.PATH, SINGLE_USE_ROW); } public static InputStream getAttachmentStream(@NonNull Context context, @NonNull Uri uri) @@ -50,7 +49,7 @@ public class PartAuthority { case PART_ROW: return DatabaseFactory.getAttachmentDatabase(context).getAttachmentStream(new PartUriParser(uri).getPartId(), 0); case THUMB_ROW: return DatabaseFactory.getAttachmentDatabase(context).getThumbnailStream(new PartUriParser(uri).getPartId()); case PERSISTENT_ROW: return PersistentBlobProvider.getInstance(context).getStream(context, ContentUris.parseId(uri)); - case SINGLE_USE_ROW: return SingleUseBlobProvider.getInstance().getStream(ContentUris.parseId(uri)); + case SINGLE_USE_ROW: return MemoryBlobProvider.getInstance().getStream(ContentUris.parseId(uri)); default: return context.getContentResolver().openInputStream(uri); } } catch (SecurityException se) { diff --git a/src/org/thoughtcrime/securesms/providers/MemoryBlobProvider.java b/src/org/thoughtcrime/securesms/providers/MemoryBlobProvider.java new file mode 100644 index 000000000..ed032b311 --- /dev/null +++ b/src/org/thoughtcrime/securesms/providers/MemoryBlobProvider.java @@ -0,0 +1,90 @@ +package org.thoughtcrime.securesms.providers; + +import android.content.ContentUris; +import android.net.Uri; +import android.support.annotation.NonNull; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Map; + +public class MemoryBlobProvider { + + @SuppressWarnings("unused") + private static final String TAG = MemoryBlobProvider.class.getSimpleName(); + + public static final String AUTHORITY = "org.thoughtcrime.securesms"; + public static final String PATH = "memory/*/#"; + private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/memory"); + + private final Map cache = new HashMap<>(); + + private static final MemoryBlobProvider instance = new MemoryBlobProvider(); + + public static MemoryBlobProvider getInstance() { + return instance; + } + + private MemoryBlobProvider() {} + + public synchronized Uri createSingleUseUri(@NonNull byte[] blob) { + return createUriInternal(blob, true); + } + + public synchronized Uri createUri(@NonNull byte[] blob) { + return createUriInternal(blob, false); + } + + public synchronized void delete(@NonNull Uri uri) { + cache.remove(ContentUris.parseId(uri)); + } + + public synchronized @NonNull InputStream getStream(long id) throws IOException { + Entry entry = cache.get(id); + + if (entry == null) { + throw new IOException("ID not found: " + id); + } + + if (entry.isSingleUse()) { + cache.remove(id); + } + + return new ByteArrayInputStream(entry.getBlob()); + } + + private Uri createUriInternal(@NonNull byte[] blob, boolean singleUse) { + try { + long id = Math.abs(SecureRandom.getInstance("SHA1PRNG").nextLong()); + cache.put(id, new Entry(blob, singleUse)); + + Uri uniqueUri = Uri.withAppendedPath(CONTENT_URI, String.valueOf(System.currentTimeMillis())); + return ContentUris.withAppendedId(uniqueUri, id); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private static class Entry { + + private final byte[] blob; + private final boolean singleUse; + + private Entry(@NonNull byte[] blob, boolean singleUse) { + this.blob = blob; + this.singleUse = singleUse; + } + + public byte[] getBlob() { + return blob; + } + + public boolean isSingleUse() { + return singleUse; + } + } +} diff --git a/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java b/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java deleted file mode 100644 index fcc839f85..000000000 --- a/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.thoughtcrime.securesms.providers; - -import android.content.ContentUris; -import android.net.Uri; -import android.support.annotation.NonNull; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.HashMap; -import java.util.Map; - -public class SingleUseBlobProvider { - - @SuppressWarnings("unused") - private static final String TAG = SingleUseBlobProvider.class.getSimpleName(); - - public static final String AUTHORITY = "org.thoughtcrime.securesms"; - public static final String PATH = "memory/*/#"; - private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/memory"); - - private final Map cache = new HashMap<>(); - - private static final SingleUseBlobProvider instance = new SingleUseBlobProvider(); - - public static SingleUseBlobProvider getInstance() { - return instance; - } - - private SingleUseBlobProvider() {} - - public synchronized Uri createUri(@NonNull byte[] blob) { - try { - long id = Math.abs(SecureRandom.getInstance("SHA1PRNG").nextLong()); - cache.put(id, blob); - - Uri uniqueUri = Uri.withAppendedPath(CONTENT_URI, String.valueOf(System.currentTimeMillis())); - return ContentUris.withAppendedId(uniqueUri, id); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - public synchronized @NonNull InputStream getStream(long id) throws IOException { - byte[] cached = cache.get(id); - cache.remove(id); - - if (cached != null) return new ByteArrayInputStream(cached); - else throw new IOException("ID not found: " + id); - - } - -}