Turned SingleUseBlobProvider into MemoryBlobProvider.

Keep the single-use behavior, but allow the creation of multi-use memory
blobs that can be deleted when we're done with them. Will help out with
having URI's for temporary images during the camera capture flow.
This commit is contained in:
Greyson Parrelli 2018-09-24 11:47:51 -07:00
parent e63773e5c8
commit e9a38bab1e
5 changed files with 98 additions and 64 deletions

View File

@ -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);
}

View File

@ -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<Address> group = Optional.absent();
Set<Address> 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());

View File

@ -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) {

View File

@ -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<Long, Entry> 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;
}
}
}

View File

@ -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<Long, byte[]> 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);
}
}