Compare commits

...

45 Commits

Author SHA1 Message Date
akwizgran
5f36ba9014 Fix tests. 2018-12-06 18:05:03 +00:00
akwizgran
438d200afe Set paths on blocks after hashing. 2018-12-06 17:43:46 +00:00
akwizgran
bd9ebe75a0 Add initial stream hashing implementation. 2018-12-06 17:43:46 +00:00
akwizgran
4b04e6a21d Add basic hash tree API and implementation. 2018-12-06 17:43:46 +00:00
akwizgran
f915eb4d36 Add BlockSink interface, using temporary message ID. 2018-12-06 17:43:41 +00:00
akwizgran
a5c9e7c74d Merge branch '1242-display-image-attachments-fullscreen' into 'master'
Add ImageActivity to show image attachment in full-screen

See merge request briar/briar!999
2018-11-30 18:04:55 +00:00
Torsten Grote
8a4a343147 [android] Move image to the top if it is overlapping the toolbar 2018-11-30 15:53:38 -02:00
Torsten Grote
7b22d3b84d [android] Address review issues for image fullscreen view 2018-11-28 17:26:01 -02:00
Torsten Grote
c8fa23273f [android] support pull down to dismiss pattern for ImageActivity 2018-11-28 17:26:01 -02:00
Torsten Grote
fbe5df8938 [android] Add ImageActivity to show images in full-screen 2018-11-28 17:26:01 -02:00
akwizgran
008cf95741 Merge branch '1467-conversation-scrolling' into 'master'
Only scroll conversation list to bottom, when already at bottom

Closes #1467

See merge request briar/briar!1000
2018-11-27 09:32:05 +00:00
Torsten Grote
3eb066a836 [android] Use new IoUtils to close InputStreams 2018-11-26 16:28:06 -02:00
Torsten Grote
674b29af25 [android] static constant all caps 2018-11-26 16:23:51 -02:00
Torsten Grote
b8ca5ab557 [android] Only scroll conversation list to bottom, when already at bottom
Closes #1467
2018-11-26 16:23:17 -02:00
Torsten Grote
6e17709f46 Merge branch 'try-to-close' into 'master'
Move tryToClose() methods into utility classes

See merge request briar/briar!1002
2018-11-26 18:22:24 +00:00
akwizgran
726d90145c Merge branch '1242-display-image-attachments' into 'master'
[android] display image attachments for conversation messages

See merge request briar/briar!997
2018-11-26 17:19:37 +00:00
Torsten Grote
165211eb9b Merge branch '1259-headless-mac-os' into 'master'
Enable headless app to start on MacOS

See merge request briar/briar!1003
2018-11-26 12:01:27 +00:00
akwizgran
868c61e5d6 Move tryToClose() methods into utility classes. 2018-11-23 15:02:27 +00:00
Torsten Grote
798bb6d4f7 [android] scale thumbnails to minimum size, don't upscale to maximum size 2018-11-23 11:25:18 -02:00
akwizgran
bc352a2dc6 Enable Tor on Mac OS once binaries are available. 2018-11-23 13:07:12 +00:00
akwizgran
ce7d6d3db5 Code cleanup. 2018-11-23 12:56:34 +00:00
akwizgran
61276c81d2 Make it possible to start the headless app on MacOS.
The app is still non-functional because we don't have a Tor plugin.
2018-11-23 12:52:40 +00:00
Torsten Grote
c09abdb088 Merge branch 'location-permission-sdk-23' into 'master'
Change location permission to uses-permission-sdk-23

See merge request briar/briar!1001
2018-11-22 12:03:07 +00:00
akwizgran
45a11badd5 Change location permission to uses-permission-sdk-23. 2018-11-20 16:16:47 +00:00
Torsten Grote
152ac3df43 [android] improve bitmap transformation hashKey and DiskCacheKey 2018-11-20 11:49:21 -02:00
Torsten Grote
dd5ad86db8 [android] Use DataFetcherFactory to create data fetchers and allow cancelling loads 2018-11-20 11:49:21 -02:00
Torsten Grote
10e9fb308d [android] Display Image Attachements: Address first round of review comments 2018-11-19 20:35:07 -02:00
Torsten Grote
de8e95692a [android] support RTL languages when rounding thumbnail corners 2018-11-19 20:35:07 -02:00
Torsten Grote
d6b52cf4ec [android] Use our own BitmapTransformation for rounded image corners 2018-11-19 20:35:07 -02:00
Torsten Grote
8a839fb5e4 [android] display image attachments for conversation messages 2018-11-19 20:35:07 -02:00
akwizgran
fbf8642edb Merge branch '1464-message-status-mixed' into 'master'
[core] fix wrong order of message status flags in conversation headers

Closes #1464

See merge request briar/briar!998
2018-11-16 13:44:39 +00:00
Torsten Grote
ade6a14342 Merge branch 'validation-refactoring' into 'master'
Reorganise validation code

See merge request briar/briar!991
2018-11-15 17:18:15 +00:00
Torsten Grote
d500ff81c3 Merge branch 'require-non-null' into 'master'
Add static requireNonNull() method

See merge request briar/briar!996
2018-11-15 16:50:16 +00:00
Torsten Grote
3053e3cfa7 [core] fix wrong order of message status flags in conversation headers 2018-11-15 14:39:05 -02:00
akwizgran
6964a67ca3 Add static requireNonNull() method. 2018-11-15 11:13:15 +00:00
Torsten Grote
f4b06e1fb3 Merge branch 'load-latest-message-eagerly' into 'master'
Load latest message eagerly

See merge request briar/briar!995
2018-11-14 16:01:59 +00:00
akwizgran
4db075d643 Only consider the latest item for preloading. 2018-11-14 15:13:25 +00:00
akwizgran
78a8ae6b8e Sort headers and eagerly load text of latest message. 2018-11-14 15:01:54 +00:00
Torsten Grote
7866037d02 Merge branch '1460-introduction-request-text' into 'master'
Show correct text when an existing contact is introduced

Closes #1460

See merge request briar/briar!994
2018-11-14 11:23:26 +00:00
akwizgran
35716051fb Show correct text when an existing contact is introduced. 2018-11-14 11:05:46 +00:00
Torsten Grote
6cafea836f Merge branch 'eager-singletons' into 'master'
Singletons that call registration methods must be eager

See merge request briar/briar!993
2018-11-13 18:03:28 +00:00
akwizgran
bd0fd229c6 Merge branch '1242-attachment-input-stream' into 'master'
Attachments will use InputStream rather than ByteBuffer

See merge request briar/briar!992
2018-11-13 17:41:39 +00:00
akwizgran
ea05a5c703 Singletons that call registration methods must be eager. 2018-11-13 17:40:06 +00:00
akwizgran
4103eaf639 Reorganise validation code (no functional changes). 2018-11-13 17:16:47 +00:00
Torsten Grote
753a25bc2a [core] Attachments will use InputStream rather than ByteBuffer 2018-11-13 15:12:34 -02:00
168 changed files with 3150 additions and 835 deletions

View File

@@ -0,0 +1,11 @@
package org.briarproject.bramble;
import org.briarproject.bramble.battery.AndroidBatteryModule;
import org.briarproject.bramble.network.AndroidNetworkModule;
public interface BrambleAndroidEagerSingletons {
void inject(AndroidBatteryModule.EagerSingletons init);
void inject(AndroidNetworkModule.EagerSingletons init);
}

View File

@@ -15,4 +15,8 @@ import dagger.Module;
})
public class BrambleAndroidModule {
public static void initEagerSingletons(BrambleAndroidEagerSingletons c) {
c.inject(new AndroidBatteryModule.EagerSingletons());
c.inject(new AndroidNetworkModule.EagerSingletons());
}
}

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.battery;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
@@ -11,6 +12,11 @@ import dagger.Provides;
@Module
public class AndroidBatteryModule {
public static class EagerSingletons {
@Inject
BatteryManager batteryManager;
}
@Provides
@Singleton
BatteryManager provideBatteryManager(LifecycleManager lifecycleManager,

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.network;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.network.NetworkManager;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
@@ -11,6 +12,11 @@ import dagger.Provides;
@Module
public class AndroidNetworkModule {
public static class EagerSingletons {
@Inject
NetworkManager networkManager;
}
@Provides
@Singleton
NetworkManager provideNetworkManager(LifecycleManager lifecycleManager,

View File

@@ -18,8 +18,8 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.IoUtils;
import java.io.Closeable;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
@@ -51,7 +51,6 @@ import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault
@@ -161,11 +160,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
@Override
void tryToClose(@Nullable BluetoothServerSocket ss) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
IoUtils.tryToClose(ss, LOG, WARNING);
}
@Override
@@ -195,7 +190,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
s.connect();
return wrapSocket(s);
} catch (IOException e) {
tryToClose(s);
IoUtils.tryToClose(s, LOG, WARNING);
throw e;
}
}
@@ -268,14 +263,6 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
return addresses;
}
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private class BluetoothStateReceiver extends BroadcastReceiver {
@Override

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.system;
import android.annotation.SuppressLint;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -26,7 +27,7 @@ import static android.provider.Settings.Secure.ANDROID_ID;
@Immutable
@NotNullByDefault
class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
private static final int SEED_LENGTH = 32;
@@ -37,6 +38,7 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
appContext = app.getApplicationContext();
}
@SuppressLint("HardwareIds")
@Override
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
super.writeToEntropyPool(out);
@@ -49,12 +51,14 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
if (id != null) out.writeUTF(id);
Parcel parcel = Parcel.obtain();
WifiManager wm =
(WifiManager) appContext.getSystemService(WIFI_SERVICE);
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
if (configs != null) {
for (WifiConfiguration config : configs)
parcel.writeParcelable(config, 0);
WifiManager wm = (WifiManager) appContext.getApplicationContext()
.getSystemService(WIFI_SERVICE);
if (wm != null) {
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
if (configs != null) {
for (WifiConfiguration config : configs)
parcel.writeParcelable(config, 0);
}
}
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt != null) {
@@ -77,13 +81,13 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private void applyOpenSslFix() {
byte[] seed = new LinuxSecureRandomSpi().engineGenerateSeed(
byte[] seed = new UnixSecureRandomSpi().engineGenerateSeed(
SEED_LENGTH);
try {
// Seed the OpenSSL PRNG
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class)
.invoke(null, seed);
.invoke(null, (Object) seed);
// Mix the output of the Linux PRNG into the OpenSSL PRNG
int bytesRead = (Integer) Class.forName(
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.client;
package org.briarproject.bramble.api.client;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser;
@@ -12,7 +11,7 @@ import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import javax.annotation.concurrent.Immutable;
@@ -62,5 +61,4 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook {
throw new InvalidMessageException(e);
}
}
}

View File

@@ -9,7 +9,7 @@ import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageContext;
import org.briarproject.bramble.api.sync.ValidationManager.MessageValidator;
import org.briarproject.bramble.api.sync.validation.MessageValidator;
import org.briarproject.bramble.api.system.Clock;
import java.util.logging.Logger;

View File

@@ -19,6 +19,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
@@ -28,8 +29,6 @@ import java.util.Map;
import javax.annotation.Nullable;
import static org.briarproject.bramble.api.sync.ValidationManager.State;
/**
* Encapsulates the database implementation and exposes high-level operations
* to other components.
@@ -374,12 +373,12 @@ public interface DatabaseComponent {
/**
* Returns the IDs and states of all dependencies of the given message.
* For missing dependencies and dependencies in other groups, the state
* {@link State UNKNOWN} is returned.
* {@link MessageState UNKNOWN} is returned.
* <p/>
* Read-only.
*/
Map<MessageId, State> getMessageDependencies(Transaction txn, MessageId m)
throws DbException;
Map<MessageId, MessageState> getMessageDependencies(Transaction txn,
MessageId m) throws DbException;
/**
* Returns the IDs and states of all dependents of the given message.
@@ -388,15 +387,16 @@ public interface DatabaseComponent {
* <p/>
* Read-only.
*/
Map<MessageId, State> getMessageDependents(Transaction txn, MessageId m)
throws DbException;
Map<MessageId, MessageState> getMessageDependents(Transaction txn,
MessageId m) throws DbException;
/**
* Gets the validation and delivery state of the given message.
* <p/>
* Read-only.
*/
State getMessageState(Transaction txn, MessageId m) throws DbException;
MessageState getMessageState(Transaction txn, MessageId m)
throws DbException;
/**
* Returns the status of the given delivered message with respect to the
@@ -543,7 +543,7 @@ public interface DatabaseComponent {
/**
* Sets the validation and delivery state of the given message.
*/
void setMessageState(Transaction txn, MessageId m, State state)
void setMessageState(Transaction txn, MessageId m, MessageState state)
throws DbException;
/**

View File

@@ -0,0 +1,20 @@
package org.briarproject.bramble.api.io;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.sync.tree.TreeHash;
import java.util.List;
public interface BlockSink {
/**
* Stores a block of the message with the given temporary ID.
*/
void putBlock(HashingId h, int blockNumber, byte[] data) throws DbException;
/**
* Sets the hash tree path of a previously stored block.
*/
void setPath(HashingId h, int blockNumber, List<TreeHash> path)
throws DbException;
}

View File

@@ -0,0 +1,27 @@
package org.briarproject.bramble.api.io;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.concurrent.ThreadSafe;
/**
* Type-safe wrapper for a byte array that uniquely identifies a
* {@link Message} while it's being hashed and the {@link MessageId} is not
* yet known.
*/
@ThreadSafe
@NotNullByDefault
public class HashingId extends UniqueId {
public HashingId(byte[] id) {
super(id);
}
@Override
public boolean equals(Object o) {
return o instanceof HashingId && super.equals(o);
}
}

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.nullsafety;
import javax.annotation.Nullable;
@NotNullByDefault
public class NullSafety {
/**
* Stand-in for `Objects.requireNonNull()`.
*/
public static <T> T requireNonNull(@Nullable T t) {
if (t == null) throw new NullPointerException();
return t;
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.tree.TreeHash;
@NotNullByDefault
public interface MessageFactory {
@@ -10,4 +11,6 @@ public interface MessageFactory {
Message createMessage(byte[] raw);
byte[] getRawMessage(Message m);
MessageId getMessageId(GroupId g, long timestamp, TreeHash rootHash);
}

View File

@@ -24,6 +24,12 @@ public class MessageId extends UniqueId {
public static final String BLOCK_LABEL =
"org.briarproject.bramble/MESSAGE_BLOCK";
/**
* Label for hashing two tree hashes to produce a parent.
*/
public static final String TREE_LABEL =
"org.briarproject.bramble/MESSAGE_TREE";
public MessageId(byte[] id) {
super(id);
}

View File

@@ -35,4 +35,9 @@ public interface SyncConstants {
* The maximum number of message IDs in an ack, offer or request record.
*/
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH;
/**
* The maximum length of a message block in bytes.
*/
int MAX_BLOCK_LENGTH = 32 * 2014; // 32 KiB
}

View File

@@ -1,87 +0,0 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Responsible for managing message validators and passing them messages to
* validate.
*/
@NotNullByDefault
public interface ValidationManager {
enum State {
UNKNOWN(0), INVALID(1), PENDING(2), DELIVERED(3);
private final int value;
State(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static State fromValue(int value) {
for (State s : values()) if (s.value == value) return s;
throw new IllegalArgumentException();
}
}
/**
* Registers the message validator for the given client. This method
* should be called before
* {@link LifecycleManager#startServices(SecretKey)}.
*/
void registerMessageValidator(ClientId c, int majorVersion,
MessageValidator v);
/**
* Registers the incoming message hook for the given client. The hook will
* be called once for each incoming message that passes validation. This
* method should be called before
* {@link LifecycleManager#startServices(SecretKey)}.
*/
void registerIncomingMessageHook(ClientId c, int majorVersion,
IncomingMessageHook hook);
interface MessageValidator {
/**
* Validates the given message and returns its metadata and
* dependencies.
*/
MessageContext validateMessage(Message m, Group g)
throws InvalidMessageException;
}
interface IncomingMessageHook {
/**
* Called once for each incoming message that passes validation.
*
* @return whether or not this message should be shared
* @throws DbException Should only be used for real database errors.
* If this is thrown, delivery will be attempted again at next startup,
* whereas if an InvalidMessageException is thrown,
* the message will be permanently invalidated.
* @throws InvalidMessageException for any non-database error
* that occurs while handling remotely created data.
* This includes errors that occur while handling locally created data
* in a context controlled by remotely created data
* (for example, parsing the metadata of a dependency
* of an incoming message).
* Throwing this will delete the incoming message and its metadata
* marking it as invalid in the database.
* Never rethrow DbException as InvalidMessageException!
*/
boolean incomingMessage(Transaction txn, Message m, Metadata meta)
throws DbException, InvalidMessageException;
}
}

View File

@@ -3,11 +3,10 @@ package org.briarproject.bramble.api.sync.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.validation.MessageState;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.sync.ValidationManager.State;
/**
* An event that is broadcast when a message state changed.
*/
@@ -17,10 +16,10 @@ public class MessageStateChangedEvent extends Event {
private final MessageId messageId;
private final boolean local;
private final State state;
private final MessageState state;
public MessageStateChangedEvent(MessageId messageId, boolean local,
State state) {
MessageState state) {
this.messageId = messageId;
this.local = local;
this.state = state;
@@ -34,7 +33,7 @@ public class MessageStateChangedEvent extends Event {
return local;
}
public State getState() {
public MessageState getState() {
return state;
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public class LeafNode extends TreeNode {
public LeafNode(TreeHash hash, int blockNumber) {
super(hash, 0, blockNumber, blockNumber);
}
@Override
public TreeNode getLeftChild() {
throw new UnsupportedOperationException();
}
@Override
public TreeNode getRightChild() {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,26 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public class ParentNode extends TreeNode {
private final TreeNode left, right;
public ParentNode(TreeHash hash, TreeNode left, TreeNode right) {
super(hash, Math.max(left.getHeight(), right.getHeight()) + 1,
left.getFirstBlockNumber(), right.getLastBlockNumber());
this.left = left;
this.right = right;
}
@Override
public TreeNode getLeftChild() {
return left;
}
@Override
public TreeNode getRightChild() {
return right;
}
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.io.BlockSink;
import org.briarproject.bramble.api.io.HashingId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
import java.io.InputStream;
@NotNullByDefault
public interface StreamHasher {
/**
* Reads the given input stream, divides the data into blocks, stores
* the blocks and the resulting hash tree using the given block sink and
* temporary ID, and returns the hash tree.
*/
TreeNode hash(InputStream in, BlockSink sink, HashingId h)
throws IOException, DbException;
}

View File

@@ -0,0 +1,24 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.ThreadSafe;
/**
* Type-safe wrapper for a byte array that uniquely identifies a sequence of
* one or more message blocks.
*/
@ThreadSafe
@NotNullByDefault
public class TreeHash extends UniqueId {
public TreeHash(byte[] id) {
super(id);
}
@Override
public boolean equals(Object o) {
return o instanceof TreeHash && super.equals(o);
}
}

View File

@@ -0,0 +1,11 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface TreeHasher {
LeafNode hashBlock(int blockNumber, byte[] data);
ParentNode mergeTrees(TreeNode left, TreeNode right);
}

View File

@@ -0,0 +1,38 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public abstract class TreeNode {
private final TreeHash hash;
private final int height, firstBlockNumber, lastBlockNumber;
TreeNode(TreeHash hash, int height, int firstBlockNumber,
int lastBlockNumber) {
this.hash = hash;
this.height = height;
this.firstBlockNumber = firstBlockNumber;
this.lastBlockNumber = lastBlockNumber;
}
public TreeHash getHash() {
return hash;
}
public int getHeight() {
return height;
}
public int getFirstBlockNumber() {
return firstBlockNumber;
}
public int getLastBlockNumber() {
return lastBlockNumber;
}
public abstract TreeNode getLeftChild();
public abstract TreeNode getRightChild();
}

View File

@@ -0,0 +1,31 @@
package org.briarproject.bramble.api.sync.validation;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
public interface IncomingMessageHook {
/**
* Called once for each incoming message that passes validation.
*
* @return whether or not this message should be shared
* @throws DbException Should only be used for real database errors.
* If this is thrown, delivery will be attempted again at next startup,
* whereas if an InvalidMessageException is thrown,
* the message will be permanently invalidated.
* @throws InvalidMessageException for any non-database error
* that occurs while handling remotely created data.
* This includes errors that occur while handling locally created data
* in a context controlled by remotely created data
* (for example, parsing the metadata of a dependency
* of an incoming message).
* Throwing this will delete the incoming message and its metadata
* marking it as invalid in the database.
* Never rethrow DbException as InvalidMessageException!
*/
boolean incomingMessage(Transaction txn, Message m, Metadata meta)
throws DbException, InvalidMessageException;
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.bramble.api.sync.validation;
public enum MessageState {
UNKNOWN(0), INVALID(1), PENDING(2), DELIVERED(3);
private final int value;
MessageState(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static MessageState fromValue(int value) {
for (MessageState s : values()) if (s.value == value) return s;
throw new IllegalArgumentException();
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.bramble.api.sync.validation;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageContext;
public interface MessageValidator {
/**
* Validates the given message and returns its metadata and
* dependencies.
*/
MessageContext validateMessage(Message m, Group g)
throws InvalidMessageException;
}

View File

@@ -0,0 +1,31 @@
package org.briarproject.bramble.api.sync.validation;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
/**
* Responsible for managing message validators and passing them messages to
* validate.
*/
@NotNullByDefault
public interface ValidationManager {
/**
* Registers the {@link MessageValidator} for the given client. This method
* should be called before
* {@link LifecycleManager#startServices(SecretKey)}.
*/
void registerMessageValidator(ClientId c, int majorVersion,
MessageValidator v);
/**
* Registers the {@link IncomingMessageHook} for the given client. The hook
* will be called once for each incoming message that passes validation.
* This method should be called before
* {@link LifecycleManager#startServices(SecretKey)}.
*/
void registerIncomingMessageHook(ClientId c, int majorVersion,
IncomingMessageHook hook);
}

View File

@@ -8,12 +8,15 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
public class IoUtils {
@@ -54,16 +57,35 @@ public class IoUtils {
out.flush();
out.close();
} catch (IOException e) {
tryToClose(in);
tryToClose(out);
tryToClose(in, LOG, WARNING);
tryToClose(out, LOG, WARNING);
}
}
private static void tryToClose(@Nullable Closeable c) {
public static void tryToClose(@Nullable Closeable c, Logger logger,
Level level) {
try {
if (c != null) c.close();
} catch (IOException e) {
// We did our best
logException(logger, level, e);
}
}
public static void tryToClose(@Nullable Socket s, Logger logger,
Level level) {
try {
if (s != null) s.close();
} catch (IOException e) {
logException(logger, level, e);
}
}
public static void tryToClose(@Nullable ServerSocket ss, Logger logger,
Level level) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
logException(logger, level, e);
}
}

View File

@@ -10,8 +10,6 @@ public class OsUtils {
@Nullable
private static final String os = System.getProperty("os.name");
@Nullable
private static final String version = System.getProperty("os.version");
@Nullable
private static final String vendor = System.getProperty("java.vendor");
public static boolean isWindows() {

View File

@@ -8,7 +8,7 @@ import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.reporting.ReportingModule;
import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.sync.validation.ValidationModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
@@ -31,11 +31,11 @@ public interface BrambleCoreEagerSingletons {
void inject(ReportingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
void inject(ValidationModule.EagerSingletons init);
void inject(VersioningModule.EagerSingletons init);
}

View File

@@ -19,6 +19,7 @@ import org.briarproject.bramble.reporting.ReportingModule;
import org.briarproject.bramble.settings.SettingsModule;
import org.briarproject.bramble.socks.SocksModule;
import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.sync.validation.ValidationModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
@@ -47,6 +48,7 @@ import dagger.Module;
SyncModule.class,
SystemModule.class,
TransportModule.class,
ValidationModule.class,
VersioningModule.class
})
public class BrambleCoreModule {
@@ -60,9 +62,9 @@ public class BrambleCoreModule {
c.inject(new PluginModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons());
c.inject(new ReportingModule.EagerSingletons());
c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons());
c.inject(new ValidationModule.EagerSingletons());
c.inject(new VersioningModule.EagerSingletons());
}
}

View File

@@ -22,7 +22,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.ValidationManager.State;
import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
@@ -106,7 +106,7 @@ interface Database<T> {
* @param sender the contact from whom the message was received, or null
* if the message was created locally.
*/
void addMessage(T txn, Message m, State state, boolean shared,
void addMessage(T txn, Message m, MessageState state, boolean shared,
@Nullable ContactId sender) throws DbException;
/**
@@ -114,7 +114,7 @@ interface Database<T> {
* in the given state.
*/
void addMessageDependency(T txn, Message dependent, MessageId dependency,
State dependentState) throws DbException;
MessageState dependentState) throws DbException;
/**
* Records that a message has been offered by the given contact.
@@ -310,11 +310,11 @@ interface Database<T> {
/**
* Returns the IDs and states of all dependencies of the given message.
* For missing dependencies and dependencies in other groups, the state
* {@link State UNKNOWN} is returned.
* {@link MessageState UNKNOWN} is returned.
* <p/>
* Read-only.
*/
Map<MessageId, State> getMessageDependencies(T txn, MessageId m)
Map<MessageId, MessageState> getMessageDependencies(T txn, MessageId m)
throws DbException;
/**
@@ -324,7 +324,7 @@ interface Database<T> {
* <p/>
* Read-only.
*/
Map<MessageId, State> getMessageDependents(T txn, MessageId m)
Map<MessageId, MessageState> getMessageDependents(T txn, MessageId m)
throws DbException;
/**
@@ -383,7 +383,7 @@ interface Database<T> {
* <p/>
* Read-only.
*/
State getMessageState(T txn, MessageId m) throws DbException;
MessageState getMessageState(T txn, MessageId m) throws DbException;
/**
* Returns the status of all delivered messages in the given group with
@@ -637,7 +637,8 @@ interface Database<T> {
/**
* Sets the validation and delivery state of the given message.
*/
void setMessageState(T txn, MessageId m, State state) throws DbException;
void setMessageState(T txn, MessageId m, MessageState state)
throws DbException;
/**
* Sets the reordering window for the given key set and transport in the

View File

@@ -43,7 +43,6 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.ValidationManager.State;
import org.briarproject.bramble.api.sync.event.GroupAddedEvent;
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
import org.briarproject.bramble.api.sync.event.GroupVisibilityUpdatedEvent;
@@ -55,6 +54,7 @@ import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
@@ -75,8 +75,8 @@ import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
@@ -579,7 +579,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public State getMessageState(Transaction transaction, MessageId m)
public MessageState getMessageState(Transaction transaction, MessageId m)
throws DbException {
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
@@ -619,8 +619,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public Map<MessageId, State> getMessageDependencies(Transaction transaction,
MessageId m) throws DbException {
public Map<MessageId, MessageState> getMessageDependencies(
Transaction transaction, MessageId m) throws DbException {
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
@@ -628,8 +628,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public Map<MessageId, State> getMessageDependents(Transaction transaction,
MessageId m) throws DbException {
public Map<MessageId, MessageState> getMessageDependents(
Transaction transaction, MessageId m) throws DbException {
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
@@ -918,7 +918,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override
public void setMessageState(Transaction transaction, MessageId m,
State state) throws DbException {
MessageState state) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
@@ -935,10 +935,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
T txn = unbox(transaction);
if (!db.containsMessage(txn, dependent.getId()))
throw new NoSuchMessageException();
State dependentState = db.getMessageState(txn, dependent.getId());
MessageState dependentState =
db.getMessageState(txn, dependent.getId());
for (MessageId dependency : dependencies) {
db.addMessageDependency(txn, dependent, dependency,
dependentState);
db.addMessageDependency(txn, dependent, dependency, dependentState);
}
}

View File

@@ -15,16 +15,23 @@ import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
/**
* Contains all the H2-specific code for the database.
*/
@NotNullByDefault
class H2Database extends JdbcDatabase {
private static final Logger LOG = getLogger(H2Database.class.getName());
private static final String HASH_TYPE = "BINARY(32)";
private static final String SECRET_TYPE = "BINARY(32)";
private static final String BINARY_TYPE = "BINARY";
@@ -121,8 +128,8 @@ class H2Database extends JdbcDatabase {
s.close();
c.close();
} catch (SQLException e) {
tryToClose(s);
tryToClose(c);
tryToClose(s, LOG, WARNING);
tryToClose(c, LOG, WARNING);
throw new DbException(e);
}
}

View File

@@ -14,16 +14,24 @@ import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
/**
* Contains all the HSQLDB-specific code for the database.
*/
@NotNullByDefault
class HyperSqlDatabase extends JdbcDatabase {
private static final Logger LOG =
getLogger(HyperSqlDatabase.class.getName());
private static final String HASH_TYPE = "BINARY(32)";
private static final String SECRET_TYPE = "BINARY(32)";
private static final String BINARY_TYPE = "BINARY";
@@ -72,8 +80,8 @@ class HyperSqlDatabase extends JdbcDatabase {
s.close();
c.close();
} catch (SQLException e) {
tryToClose(s);
tryToClose(c);
tryToClose(s, LOG, WARNING);
tryToClose(c, LOG, WARNING);
throw new DbException(e);
}
}
@@ -122,8 +130,8 @@ class HyperSqlDatabase extends JdbcDatabase {
s.close();
c.close();
} catch (SQLException e) {
tryToClose(s);
tryToClose(c);
tryToClose(s, LOG, WARNING);
tryToClose(c, LOG, WARNING);
throw new DbException(e);
}
}

View File

@@ -24,7 +24,7 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.ValidationManager.State;
import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySet;
@@ -64,14 +64,15 @@ import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
@@ -458,30 +459,6 @@ abstract class JdbcDatabase implements Database<Connection> {
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
}
private void tryToClose(@Nullable ResultSet rs) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
protected void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
protected void tryToClose(@Nullable Connection c) {
try {
if (c != null) c.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
private void createTables(Connection txn) throws DbException {
Statement s = null;
try {
@@ -502,7 +479,7 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
s.close();
} catch (SQLException e) {
tryToClose(s);
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
@@ -519,7 +496,7 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
s.close();
} catch (SQLException e) {
tryToClose(s);
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
@@ -566,11 +543,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} catch (SQLException e) {
// Try to close the connection
logException(LOG, WARNING, e);
try {
txn.close();
} catch (SQLException e1) {
logException(LOG, WARNING, e1);
}
tryToClose(txn, LOG, WARNING);
// Whatever happens, allow the database to close
connectionsLock.lock();
try {
@@ -659,8 +632,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return c;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -681,7 +654,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -704,7 +677,7 @@ abstract class JdbcDatabase implements Database<Connection> {
// Create a status row for each message in the group
addStatus(txn, c, g, groupShared);
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -724,7 +697,7 @@ abstract class JdbcDatabase implements Database<Connection> {
while (rs.next()) {
MessageId id = new MessageId(rs.getBytes(1));
long timestamp = rs.getLong(2);
State state = State.fromValue(rs.getInt(3));
MessageState state = MessageState.fromValue(rs.getInt(3));
boolean messageShared = rs.getBoolean(4);
int length = rs.getInt(5);
boolean deleted = rs.getBoolean(6);
@@ -735,8 +708,8 @@ abstract class JdbcDatabase implements Database<Connection> {
rs.close();
ps.close();
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -761,13 +734,13 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public void addMessage(Connection txn, Message m, State state,
public void addMessage(Connection txn, Message m, MessageState state,
boolean messageShared, @Nullable ContactId sender)
throws DbException {
PreparedStatement ps = null;
@@ -810,7 +783,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -840,14 +813,14 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
private void addStatus(Connection txn, MessageId m, ContactId c, GroupId g,
long timestamp, int length, State state, boolean groupShared,
long timestamp, int length, MessageState state, boolean groupShared,
boolean messageShared, boolean deleted, boolean seen)
throws DbException {
PreparedStatement ps = null;
@@ -873,14 +846,15 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public void addMessageDependency(Connection txn, Message dependent,
MessageId dependency, State dependentState) throws DbException {
MessageId dependency, MessageState dependentState)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
@@ -891,9 +865,9 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(1, dependency.getBytes());
ps.setBytes(2, dependent.getGroupId().getBytes());
rs = ps.executeQuery();
State dependencyState = null;
MessageState dependencyState = null;
if (rs.next()) {
dependencyState = State.fromValue(rs.getInt(1));
dependencyState = MessageState.fromValue(rs.getInt(1));
if (rs.next()) throw new DbStateException();
}
rs.close();
@@ -914,8 +888,8 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -934,7 +908,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1014,8 +988,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return keySetId;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1038,8 +1012,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return found;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1060,8 +1034,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return found;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1082,8 +1056,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return found;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1104,8 +1078,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return found;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1126,8 +1100,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return found;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1148,8 +1122,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return found;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1173,8 +1147,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return found;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1197,8 +1171,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return count;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1222,7 +1196,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1239,7 +1213,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1272,8 +1246,8 @@ abstract class JdbcDatabase implements Database<Connection> {
return new Contact(c, author, localAuthorId, alias, verified,
active);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1309,8 +1283,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return contacts;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1332,8 +1306,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1370,8 +1344,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return contacts;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1394,8 +1368,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return new Group(g, clientId, majorVersion, descriptor);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1422,8 +1396,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return groups;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1448,8 +1422,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return v;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1472,8 +1446,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return visible;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1504,8 +1478,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return localAuthor;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1536,8 +1510,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return authors;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1565,8 +1539,8 @@ abstract class JdbcDatabase implements Database<Connection> {
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
return new Message(m, g, timestamp, body);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1589,8 +1563,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1627,8 +1601,8 @@ abstract class JdbcDatabase implements Database<Connection> {
if (intersection == null) throw new AssertionError();
return intersection;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1660,8 +1634,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return all;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1695,8 +1669,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return metadata;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1719,8 +1693,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return metadata;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1745,8 +1719,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return metadata;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1775,8 +1749,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return statuses;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1806,14 +1780,14 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return status;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public Map<MessageId, State> getMessageDependencies(Connection txn,
public Map<MessageId, MessageState> getMessageDependencies(Connection txn,
MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
@@ -1824,10 +1798,10 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
rs = ps.executeQuery();
Map<MessageId, State> dependencies = new HashMap<>();
Map<MessageId, MessageState> dependencies = new HashMap<>();
while (rs.next()) {
MessageId dependency = new MessageId(rs.getBytes(1));
State state = State.fromValue(rs.getInt(2));
MessageState state = MessageState.fromValue(rs.getInt(2));
if (rs.wasNull())
state = UNKNOWN; // Missing or in a different group
dependencies.put(dependency, state);
@@ -1836,14 +1810,14 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return dependencies;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public Map<MessageId, State> getMessageDependents(Connection txn,
public Map<MessageId, MessageState> getMessageDependents(Connection txn,
MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
@@ -1857,24 +1831,24 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
rs = ps.executeQuery();
Map<MessageId, State> dependents = new HashMap<>();
Map<MessageId, MessageState> dependents = new HashMap<>();
while (rs.next()) {
MessageId dependent = new MessageId(rs.getBytes(1));
State state = State.fromValue(rs.getInt(2));
MessageState state = MessageState.fromValue(rs.getInt(2));
dependents.put(dependent, state);
}
rs.close();
ps.close();
return dependents;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public State getMessageState(Connection txn, MessageId m)
public MessageState getMessageState(Connection txn, MessageId m)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
@@ -1884,14 +1858,14 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(1, m.getBytes());
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
State state = State.fromValue(rs.getInt(1));
MessageState state = MessageState.fromValue(rs.getInt(1));
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
return state;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1915,8 +1889,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1949,8 +1923,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -1974,8 +1948,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2013,8 +1987,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2032,7 +2006,7 @@ abstract class JdbcDatabase implements Database<Connection> {
}
private Collection<MessageId> getMessagesInState(Connection txn,
State state) throws DbException {
MessageState state) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
@@ -2047,8 +2021,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2075,8 +2049,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2105,8 +2079,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return nextSendTime;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2144,8 +2118,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2167,8 +2141,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return s;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2233,8 +2207,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return keys;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2253,7 +2227,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2280,7 +2254,7 @@ abstract class JdbcDatabase implements Database<Connection> {
}
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2307,7 +2281,7 @@ abstract class JdbcDatabase implements Database<Connection> {
}
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2337,7 +2311,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2359,7 +2333,7 @@ abstract class JdbcDatabase implements Database<Connection> {
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
GroupId g = new GroupId(rs.getBytes(1));
State state = State.fromValue(rs.getInt(2));
MessageState state = MessageState.fromValue(rs.getInt(2));
rs.close();
ps.close();
// Insert any keys that don't already exist
@@ -2382,8 +2356,8 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2449,7 +2423,7 @@ abstract class JdbcDatabase implements Database<Connection> {
}
return added;
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2496,7 +2470,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2515,7 +2489,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2534,7 +2508,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2553,7 +2527,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2570,7 +2544,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2586,7 +2560,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2614,7 +2588,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2631,7 +2605,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2647,7 +2621,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2666,7 +2640,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
return affected == 1;
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2691,7 +2665,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2708,7 +2682,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2729,7 +2703,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2748,7 +2722,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2766,7 +2740,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2784,7 +2758,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2803,7 +2777,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2833,7 +2807,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2859,13 +2833,13 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public void setMessageState(Connection txn, MessageId m, State state)
public void setMessageState(Connection txn, MessageId m, MessageState state)
throws DbException {
PreparedStatement ps = null;
try {
@@ -2912,7 +2886,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2935,7 +2909,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2954,7 +2928,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -2990,8 +2964,8 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@@ -3058,7 +3032,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows < 0 || rows > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}

View File

@@ -0,0 +1,42 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class JdbcUtils {
static void tryToClose(@Nullable ResultSet rs, Logger logger, Level level) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
logException(logger, level, e);
}
}
static void tryToClose(@Nullable Statement s, Logger logger, Level level) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(logger, level, e);
}
}
static void tryToClose(@Nullable Connection c, Logger logger, Level level) {
try {
if (c != null) c.close();
} catch (SQLException e) {
logException(logger, level, e);
}
}
}

View File

@@ -7,10 +7,8 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration38_39 implements Migration<Connection> {
@@ -40,16 +38,8 @@ class Migration38_39 implements Migration<Connection> {
+ " ALTER COLUMN contactId"
+ " SET NOT NULL");
} catch (SQLException e) {
tryToClose(s);
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
private void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -7,10 +7,8 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration39_40 implements Migration<Connection> {
@@ -39,16 +37,8 @@ class Migration39_40 implements Migration<Connection> {
+ " ALTER COLUMN eta"
+ " SET NOT NULL");
} catch (SQLException e) {
tryToClose(s);
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
private void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -7,11 +7,9 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration40_41 implements Migration<Connection> {
@@ -41,16 +39,8 @@ class Migration40_41 implements Migration<Connection> {
s.execute("ALTER TABLE contacts"
+ dbTypes.replaceTypes(" ADD alias _STRING"));
} catch (SQLException e) {
tryToClose(s);
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
private void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -4,12 +4,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
@NotNullByDefault
class FileTransportReader implements TransportConnectionReader {
@@ -34,11 +33,7 @@ class FileTransportReader implements TransportConnectionReader {
@Override
public void dispose(boolean exception, boolean recognised) {
try {
in.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
tryToClose(in, LOG, WARNING);
plugin.readerFinished(file, exception, recognised);
}
}

View File

@@ -4,12 +4,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
@NotNullByDefault
class FileTransportWriter implements TransportConnectionWriter {
@@ -44,11 +43,7 @@ class FileTransportWriter implements TransportConnectionWriter {
@Override
public void dispose(boolean exception) {
try {
out.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
tryToClose(out, LOG, WARNING);
plugin.writerFinished(file, exception);
}
}

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils;
import java.io.IOException;
@@ -35,7 +36,6 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
@NotNullByDefault
@@ -293,11 +293,7 @@ class LanTcpPlugin extends TcpPlugin {
@Override
public void close() {
try {
ss.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
IoUtils.tryToClose(ss, LOG, WARNING);
}
}

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils;
import java.io.IOException;
@@ -153,13 +154,8 @@ abstract class TcpPlugin implements DuplexPlugin {
}
protected void tryToClose(@Nullable ServerSocket ss) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
} finally {
callback.transportDisabled();
}
IoUtils.tryToClose(ss, LOG, WARNING);
callback.transportDisabled();
}
String getIpPortString(InetSocketAddress a) {

View File

@@ -31,7 +31,6 @@ import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.util.IoUtils;
import java.io.Closeable;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
@@ -303,8 +302,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
IoUtils.copyAndClose(in, out);
doneFile.createNewFile();
} catch (IOException e) {
tryToClose(in);
tryToClose(out);
IoUtils.tryToClose(in, LOG, WARNING);
IoUtils.tryToClose(out, LOG, WARNING);
throw new PluginException(e);
}
}
@@ -341,22 +340,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return getClass().getClassLoader().getResourceAsStream("torrc");
}
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void tryToClose(@Nullable Socket s) {
try {
if (s != null) s.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void listFiles(File f) {
if (f.isDirectory()) {
File[] children = f.listFiles();
@@ -378,7 +361,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
return b;
} finally {
tryToClose(in);
IoUtils.tryToClose(in, LOG, WARNING);
}
}
@@ -418,13 +401,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
private void tryToClose(@Nullable ServerSocket ss) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
} finally {
callback.transportDisabled();
}
IoUtils.tryToClose(ss, LOG, WARNING);
callback.transportDisabled();
}
private void publishHiddenService(String port) {
@@ -593,7 +571,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
LOG.info("Could not connect to " + scrubOnion(bestOnion)
+ ": " + e.toString());
}
tryToClose(s);
IoUtils.tryToClose(s, LOG, WARNING);
return null;
}
}

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.sync.validation.ValidationManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;

View File

@@ -24,7 +24,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;

View File

@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -26,13 +25,12 @@ import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.net.SocketFactory;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
@Immutable
@NotNullByDefault
@@ -66,7 +64,7 @@ class DevReporterImpl implements DevReporter, EventListener {
s.setSoTimeout(SOCKET_TIMEOUT);
return s;
} catch (IOException e) {
tryToClose(s);
tryToClose(s, LOG, WARNING);
throw e;
}
}
@@ -88,8 +86,7 @@ class DevReporterImpl implements DevReporter, EventListener {
writer.append(armoured);
writer.flush();
} finally {
if (writer != null)
writer.close();
tryToClose(writer, LOG, WARNING);
}
}
@@ -121,27 +118,11 @@ class DevReporterImpl implements DevReporter, EventListener {
f.delete();
} catch (IOException e) {
LOG.log(WARNING, "Failed to send reports", e);
tryToClose(out);
tryToClose(in);
tryToClose(out, LOG, WARNING);
tryToClose(in, LOG, WARNING);
return;
}
}
LOG.info("Reports sent");
}
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void tryToClose(@Nullable Socket s) {
try {
if (s != null) s.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -7,6 +7,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.tree.TreeHash;
import org.briarproject.bramble.util.ByteUtils;
import javax.annotation.concurrent.Immutable;
@@ -39,13 +40,19 @@ class MessageFactoryImpl implements MessageFactory {
if (body.length == 0) throw new IllegalArgumentException();
if (body.length > MAX_MESSAGE_BODY_LENGTH)
throw new IllegalArgumentException();
MessageId id = getMessageId(g, timestamp, body);
MessageId id = getMessageIdFromBody(g, timestamp, body);
return new Message(id, g, timestamp, body);
}
private MessageId getMessageId(GroupId g, long timestamp, byte[] body) {
private MessageId getMessageIdFromBody(GroupId g, long timestamp,
byte[] body) {
// There's only one block, so the root hash is the hash of the block
byte[] rootHash = crypto.hash(BLOCK_LABEL, FORMAT_VERSION_BYTES, body);
return getMessageIdFromRootHash(g, timestamp, rootHash);
}
private MessageId getMessageIdFromRootHash(GroupId g, long timestamp,
byte[] rootHash) {
byte[] timeBytes = new byte[INT_64_BYTES];
ByteUtils.writeUint64(timestamp, timeBytes, 0);
byte[] idHash = crypto.hash(ID_LABEL, FORMAT_VERSION_BYTES,
@@ -65,7 +72,7 @@ class MessageFactoryImpl implements MessageFactory {
long timestamp = ByteUtils.readUint64(raw, UniqueId.LENGTH);
byte[] body = new byte[raw.length - MESSAGE_HEADER_LENGTH];
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
MessageId id = getMessageId(g, timestamp, body);
MessageId id = getMessageIdFromBody(g, timestamp, body);
return new Message(id, g, timestamp, body);
}
@@ -78,4 +85,10 @@ class MessageFactoryImpl implements MessageFactory {
System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length);
return raw;
}
@Override
public MessageId getMessageId(GroupId g, long timestamp,
TreeHash rootHash) {
return getMessageIdFromRootHash(g, timestamp, rootHash.getBytes());
}
}

View File

@@ -1,23 +1,11 @@
package org.briarproject.bramble.sync;
import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.GroupFactory;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.SyncRecordReaderFactory;
import org.briarproject.bramble.api.sync.SyncRecordWriterFactory;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
@@ -26,29 +14,14 @@ import dagger.Provides;
@Module
public class SyncModule {
public static class EagerSingletons {
@Inject
ValidationManager validationManager;
}
/**
* The maximum number of validation tasks to delegate to the crypto
* executor concurrently.
* <p>
* The number of available processors can change during the lifetime of the
* JVM, so this is just a reasonable guess.
*/
private static final int MAX_CONCURRENT_VALIDATION_TASKS =
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
@Provides
GroupFactory provideGroupFactory(CryptoComponent crypto) {
return new GroupFactoryImpl(crypto);
GroupFactory provideGroupFactory(GroupFactoryImpl groupFactory) {
return groupFactory;
}
@Provides
MessageFactory provideMessageFactory(CryptoComponent crypto) {
return new MessageFactoryImpl(crypto);
MessageFactory provideMessageFactory(MessageFactoryImpl messageFactory) {
return messageFactory;
}
@Provides
@@ -65,30 +38,8 @@ public class SyncModule {
@Provides
@Singleton
SyncSessionFactory provideSyncSessionFactory(DatabaseComponent db,
@DatabaseExecutor Executor dbExecutor, EventBus eventBus,
Clock clock, SyncRecordReaderFactory recordReaderFactory,
SyncRecordWriterFactory recordWriterFactory) {
return new SyncSessionFactoryImpl(db, dbExecutor, eventBus, clock,
recordReaderFactory, recordWriterFactory);
}
@Provides
@Singleton
ValidationManager provideValidationManager(
LifecycleManager lifecycleManager, EventBus eventBus,
ValidationManagerImpl validationManager) {
lifecycleManager.registerService(validationManager);
eventBus.addListener(validationManager);
return validationManager;
}
@Provides
@Singleton
@ValidationExecutor
Executor provideValidationExecutor(
@CryptoExecutor Executor cryptoExecutor) {
return new PoliteExecutor("ValidationExecutor", cryptoExecutor,
MAX_CONCURRENT_VALIDATION_TASKS);
SyncSessionFactory provideSyncSessionFactory(
SyncSessionFactoryImpl syncSessionFactory) {
return syncSessionFactory;
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.bramble.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.tree.LeafNode;
import org.briarproject.bramble.api.sync.tree.TreeNode;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
@NotNullByDefault
interface HashTree {
void addLeaf(LeafNode leaf);
TreeNode getRoot();
}

View File

@@ -0,0 +1,46 @@
package org.briarproject.bramble.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.tree.LeafNode;
import org.briarproject.bramble.api.sync.tree.TreeHasher;
import org.briarproject.bramble.api.sync.tree.TreeNode;
import java.util.Deque;
import java.util.LinkedList;
import javax.inject.Inject;
@NotNullByDefault
class HashTreeImpl implements HashTree {
private final TreeHasher treeHasher;
private final Deque<TreeNode> nodes = new LinkedList<>();
@Inject
HashTreeImpl(TreeHasher treeHasher) {
this.treeHasher = treeHasher;
}
@Override
public void addLeaf(LeafNode leaf) {
TreeNode add = leaf;
int height = leaf.getHeight();
TreeNode last = nodes.peekLast();
while (last != null && last.getHeight() == height) {
add = treeHasher.mergeTrees(last, add);
height = add.getHeight();
nodes.removeLast();
last = nodes.peekLast();
}
nodes.addLast(add);
}
@Override
public TreeNode getRoot() {
TreeNode root = nodes.removeLast();
while (!nodes.isEmpty()) {
root = treeHasher.mergeTrees(nodes.removeLast(), root);
}
return root;
}
}

View File

@@ -0,0 +1,85 @@
package org.briarproject.bramble.sync.tree;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.io.BlockSink;
import org.briarproject.bramble.api.io.HashingId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.tree.StreamHasher;
import org.briarproject.bramble.api.sync.tree.TreeHash;
import org.briarproject.bramble.api.sync.tree.TreeHasher;
import org.briarproject.bramble.api.sync.tree.TreeNode;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.inject.Provider;
import static java.util.Arrays.copyOfRange;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_BLOCK_LENGTH;
@Immutable
@NotNullByDefault
class StreamHasherImpl implements StreamHasher {
private final TreeHasher treeHasher;
private final Provider<HashTree> hashTreeProvider;
@Inject
StreamHasherImpl(TreeHasher treeHasher,
Provider<HashTree> hashTreeProvider) {
this.treeHasher = treeHasher;
this.hashTreeProvider = hashTreeProvider;
}
@Override
public TreeNode hash(InputStream in, BlockSink sink, HashingId h)
throws IOException, DbException {
HashTree tree = hashTreeProvider.get();
byte[] block = new byte[MAX_BLOCK_LENGTH];
int read;
for (int blockNumber = 0; (read = read(in, block)) > 0; blockNumber++) {
byte[] data;
if (read == block.length) data = block;
else data = copyOfRange(block, 0, read);
sink.putBlock(h, blockNumber, data);
tree.addLeaf(treeHasher.hashBlock(blockNumber, data));
}
TreeNode root = tree.getRoot();
setPaths(sink, h, root, new LinkedList<>());
return root;
}
/**
* Reads a block from the given input stream and returns the number of
* bytes read, or 0 if no bytes were read before reaching the end of the
* stream.
*/
private int read(InputStream in, byte[] block) throws IOException {
int offset = 0;
while (offset < block.length) {
int read = in.read(block, offset, block.length - offset);
if (read == -1) return offset;
offset += read;
}
return offset;
}
private void setPaths(BlockSink sink, HashingId h, TreeNode node,
LinkedList<TreeHash> path) throws DbException {
if (node.getHeight() == 0) {
// We've reached a leaf - store the path
sink.setPath(h, node.getFirstBlockNumber(), path);
} else {
// Add the right child's hash to the path and traverse the left
path.addFirst(node.getRightChild().getHash());
setPaths(sink, h, node.getLeftChild(), path);
// Add the left child's hash to the path and traverse the right
path.removeFirst();
path.addFirst(node.getLeftChild().getHash());
setPaths(sink, h, node.getRightChild(), path);
}
}
}

View File

@@ -0,0 +1,44 @@
package org.briarproject.bramble.sync.tree;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.tree.LeafNode;
import org.briarproject.bramble.api.sync.tree.ParentNode;
import org.briarproject.bramble.api.sync.tree.TreeHash;
import org.briarproject.bramble.api.sync.tree.TreeHasher;
import org.briarproject.bramble.api.sync.tree.TreeNode;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.Message.FORMAT_VERSION;
import static org.briarproject.bramble.api.sync.MessageId.BLOCK_LABEL;
import static org.briarproject.bramble.api.sync.MessageId.TREE_LABEL;
@Immutable
@NotNullByDefault
class TreeHasherImpl implements TreeHasher {
private static final byte[] FORMAT_VERSION_BYTES =
new byte[] {FORMAT_VERSION};
private final CryptoComponent crypto;
@Inject
TreeHasherImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
@Override
public LeafNode hashBlock(int blockNumber, byte[] data) {
byte[] hash = crypto.hash(BLOCK_LABEL, FORMAT_VERSION_BYTES, data);
return new LeafNode(new TreeHash(hash), blockNumber);
}
@Override
public ParentNode mergeTrees(TreeNode left, TreeNode right) {
byte[] hash = crypto.hash(TREE_LABEL, FORMAT_VERSION_BYTES,
left.getHash().getBytes(), right.getHash().getBytes());
return new ParentNode(new TreeHash(hash), left, right);
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.bramble.sync;
package org.briarproject.bramble.sync.validation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

View File

@@ -1,4 +1,4 @@
package org.briarproject.bramble.sync;
package org.briarproject.bramble.sync.validation;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -18,8 +18,11 @@ import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageContext;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.sync.event.MessageAddedEvent;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.sync.validation.MessageValidator;
import org.briarproject.bramble.api.sync.validation.ValidationManager;
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
import java.util.Collection;
@@ -37,9 +40,9 @@ import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.bramble.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
import static org.briarproject.bramble.util.LogUtils.logException;
@ThreadSafe
@@ -166,9 +169,9 @@ class ValidationManagerImpl implements ValidationManager, Service,
// Check if message is still pending
if (db.getMessageState(txn, id) == PENDING) {
// Check if dependencies are valid and delivered
Map<MessageId, State> states =
Map<MessageId, MessageState> states =
db.getMessageDependencies(txn, id);
for (Entry<MessageId, State> e : states.entrySet()) {
for (Entry<MessageId, MessageState> e : states.entrySet()) {
if (e.getValue() == INVALID) anyInvalid = true;
if (e.getValue() != DELIVERED) allDelivered = false;
}
@@ -256,9 +259,9 @@ class ValidationManagerImpl implements ValidationManager, Service,
if (!dependencies.isEmpty()) {
db.addMessageDependencies(txn, m, dependencies);
// Check if dependencies are valid and delivered
Map<MessageId, State> states =
Map<MessageId, MessageState> states =
db.getMessageDependencies(txn, id);
for (Entry<MessageId, State> e : states.entrySet()) {
for (Entry<MessageId, MessageState> e : states.entrySet()) {
if (e.getValue() == INVALID) anyInvalid = true;
if (e.getValue() != DELIVERED) allDelivered = false;
}
@@ -322,8 +325,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
@DatabaseExecutor
private void addPendingDependents(Transaction txn, MessageId m,
Queue<MessageId> pending) throws DbException {
Map<MessageId, State> states = db.getMessageDependents(txn, m);
for (Entry<MessageId, State> e : states.entrySet()) {
Map<MessageId, MessageState> states = db.getMessageDependents(txn, m);
for (Entry<MessageId, MessageState> e : states.entrySet()) {
if (e.getValue() == PENDING) pending.add(e.getKey());
}
}
@@ -411,8 +414,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
@DatabaseExecutor
private void addDependentsToInvalidate(Transaction txn,
MessageId m, Queue<MessageId> invalidate) throws DbException {
Map<MessageId, State> states = db.getMessageDependents(txn, m);
for (Entry<MessageId, State> e : states.entrySet()) {
Map<MessageId, MessageState> states = db.getMessageDependents(txn, m);
for (Entry<MessageId, MessageState> e : states.entrySet()) {
if (e.getValue() != INVALID) invalidate.add(e.getKey());
}
}

View File

@@ -0,0 +1,53 @@
package org.briarproject.bramble.sync.validation;
import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.validation.ValidationManager;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class ValidationModule {
public static class EagerSingletons {
@Inject
ValidationManager validationManager;
}
/**
* The maximum number of validation tasks to delegate to the crypto
* executor concurrently.
* <p>
* The number of available processors can change during the lifetime of the
* JVM, so this is just a reasonable guess.
*/
private static final int MAX_CONCURRENT_VALIDATION_TASKS =
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
@Provides
@Singleton
ValidationManager provideValidationManager(
LifecycleManager lifecycleManager, EventBus eventBus,
ValidationManagerImpl validationManager) {
lifecycleManager.registerService(validationManager);
eventBus.addListener(validationManager);
return validationManager;
}
@Provides
@Singleton
@ValidationExecutor
Executor provideValidationExecutor(
@CryptoExecutor Executor cryptoExecutor) {
return new PoliteExecutor("ValidationExecutor", cryptoExecutor,
MAX_CONCURRENT_VALIDATION_TASKS);
}
}

View File

@@ -13,32 +13,33 @@ import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@Immutable
@NotNullByDefault
class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
class UnixSecureRandomProvider extends AbstractSecureRandomProvider {
private static final Logger LOG =
Logger.getLogger(LinuxSecureRandomProvider.class.getName());
getLogger(UnixSecureRandomProvider.class.getName());
private static final File RANDOM_DEVICE = new File("/dev/urandom");
private final AtomicBoolean seeded = new AtomicBoolean(false);
private final File outputDevice;
LinuxSecureRandomProvider() {
UnixSecureRandomProvider() {
this(RANDOM_DEVICE);
}
LinuxSecureRandomProvider(File outputDevice) {
UnixSecureRandomProvider(File outputDevice) {
this.outputDevice = outputDevice;
}
@Override
public Provider getProvider() {
if (!seeded.getAndSet(true)) writeSeed();
return new LinuxProvider();
return new UnixProvider();
}
protected void writeSeed() {
@@ -55,15 +56,15 @@ class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
}
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private static class LinuxProvider extends Provider {
private static class UnixProvider extends Provider {
private LinuxProvider() {
super("LinuxPRNG", 1.1, "A Linux-specific PRNG using /dev/urandom");
private UnixProvider() {
super("UnixPRNG", 1.0, "A Unix-specific PRNG using /dev/urandom");
// Although /dev/urandom is not a SHA-1 PRNG, some callers
// explicitly request a SHA1PRNG SecureRandom and we need to
// prevent them from getting the default implementation whose
// output may have low entropy.
put("SecureRandom.SHA1PRNG", LinuxSecureRandomSpi.class.getName());
put("SecureRandom.SHA1PRNG", UnixSecureRandomSpi.class.getName());
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
}
}

View File

@@ -10,22 +10,24 @@ import java.security.SecureRandomSpi;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
public class LinuxSecureRandomSpi extends SecureRandomSpi {
public class UnixSecureRandomSpi extends SecureRandomSpi {
private static final Logger LOG =
Logger.getLogger(LinuxSecureRandomSpi.class.getName());
getLogger(UnixSecureRandomSpi.class.getName());
private static final File RANDOM_DEVICE = new File("/dev/urandom");
private final File inputDevice, outputDevice;
public LinuxSecureRandomSpi() {
@SuppressWarnings("WeakerAccess")
public UnixSecureRandomSpi() {
this(RANDOM_DEVICE, RANDOM_DEVICE);
}
LinuxSecureRandomSpi(File inputDevice, File outputDevice) {
UnixSecureRandomSpi(File inputDevice, File outputDevice) {
this.inputDevice = inputDevice;
this.outputDevice = outputDevice;
}

View File

@@ -23,7 +23,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;

View File

@@ -4,7 +4,7 @@ import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.sync.validation.ValidationManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;

View File

@@ -64,8 +64,8 @@ import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
import static org.briarproject.bramble.test.TestUtils.getAuthor;

View File

@@ -11,7 +11,7 @@ import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.ValidationManager.State;
import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.UTest;
import org.junit.After;
@@ -33,7 +33,7 @@ import java.util.logging.Logger;
import static java.util.logging.Level.OFF;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getGroup;
@@ -565,7 +565,8 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
for (int k = 0; k < MESSAGES_PER_GROUP; k++) {
Message m = getMessage(g.getId());
messages.add(m);
State state = State.fromValue(random.nextInt(4));
MessageState state =
MessageState.fromValue(random.nextInt(4));
boolean shared = random.nextBoolean();
ContactId sender = random.nextBoolean() ? c : null;
db.addMessage(txn, m, state, shared, sender);

View File

@@ -18,7 +18,7 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.ValidationManager.State;
import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySet;
@@ -58,10 +58,10 @@ import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.bramble.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
@@ -1309,7 +1309,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessageDependency(txn, message1, messageId3, PENDING);
db.addMessageDependency(txn, message2, messageId4, INVALID);
Map<MessageId, State> dependencies;
Map<MessageId, MessageState> dependencies;
// Retrieve dependencies for root
dependencies = db.getMessageDependencies(txn, messageId);
@@ -1333,7 +1333,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
dependencies = db.getMessageDependencies(txn, messageId4);
assertEquals(0, dependencies.size());
Map<MessageId, State> dependents;
Map<MessageId, MessageState> dependents;
// Root message does not have dependents
dependents = db.getMessageDependents(txn, messageId);
@@ -1408,7 +1408,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessageDependency(txn, message, messageId3, PENDING);
// Retrieve the dependencies for the root
Map<MessageId, State> dependencies;
Map<MessageId, MessageState> dependencies;
dependencies = db.getMessageDependencies(txn, messageId);
// The cross-group dependency should have state UNKNOWN
@@ -1421,7 +1421,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(DELIVERED, dependencies.get(messageId3));
// Retrieve the dependents for the message in the second group
Map<MessageId, State> dependents;
Map<MessageId, MessageState> dependents;
dependents = db.getMessageDependents(txn, messageId1);
// The cross-group dependent should be excluded

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.sync;
import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.record.RecordModule;
import org.briarproject.bramble.sync.validation.ValidationModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.test.TestSecureRandomModule;
import org.briarproject.bramble.transport.TransportModule;
@@ -17,6 +18,7 @@ import dagger.Component;
RecordModule.class,
SyncModule.class,
SystemModule.class,
ValidationModule.class,
TransportModule.class
})
interface SyncIntegrationTestComponent {

View File

@@ -1,4 +1,4 @@
package org.briarproject.bramble.sync;
package org.briarproject.bramble.sync.validation;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -13,10 +13,10 @@ import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageContext;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook;
import org.briarproject.bramble.api.sync.ValidationManager.MessageValidator;
import org.briarproject.bramble.api.sync.ValidationManager.State;
import org.briarproject.bramble.api.sync.event.MessageAddedEvent;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.sync.validation.MessageValidator;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations;
import org.briarproject.bramble.test.ImmediateExecutor;
@@ -32,10 +32,10 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.bramble.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getMessage;
@@ -559,7 +559,7 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
public void testRecursiveInvalidation() throws Exception {
MessageId messageId3 = new MessageId(getRandomId());
MessageId messageId4 = new MessageId(getRandomId());
Map<MessageId, State> twoDependents = new LinkedHashMap<>();
Map<MessageId, MessageState> twoDependents = new LinkedHashMap<>();
twoDependents.put(messageId1, PENDING);
twoDependents.put(messageId2, PENDING);
Transaction txn = new Transaction(null, true);
@@ -643,10 +643,10 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
Message message4 = getMessage(groupId);
MessageId messageId3 = message3.getId();
MessageId messageId4 = message4.getId();
Map<MessageId, State> twoDependents = new LinkedHashMap<>();
Map<MessageId, MessageState> twoDependents = new LinkedHashMap<>();
twoDependents.put(messageId1, PENDING);
twoDependents.put(messageId2, PENDING);
Map<MessageId, State> twoDependencies = new LinkedHashMap<>();
Map<MessageId, MessageState> twoDependencies = new LinkedHashMap<>();
twoDependencies.put(messageId1, DELIVERED);
twoDependencies.put(messageId2, DELIVERED);
Transaction txn = new Transaction(null, true);
@@ -765,7 +765,7 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
@Test
public void testOnlyReadyPendingDependentsGetDelivered() throws Exception {
Map<MessageId, State> twoDependencies = new LinkedHashMap<>();
Map<MessageId, MessageState> twoDependencies = new LinkedHashMap<>();
twoDependencies.put(messageId, DELIVERED);
twoDependencies.put(messageId2, UNKNOWN);
Transaction txn = new Transaction(null, true);

View File

@@ -1,8 +1,6 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.OsUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -10,48 +8,50 @@ import org.junit.Test;
import java.io.File;
import java.security.Provider;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.util.OsUtils.isLinux;
import static org.briarproject.bramble.util.OsUtils.isMac;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
public class LinuxSecureRandomProviderTest extends BrambleTestCase {
public class UnixSecureRandomProviderTest extends BrambleTestCase {
private final File testDir = TestUtils.getTestDirectory();
private final File testDir = getTestDirectory();
@Before
public void setUp() {
testDir.mkdirs();
assumeTrue(isLinux() || isMac());
assertTrue(testDir.mkdirs());
}
@Test
public void testGetProviderWritesToRandomDeviceOnFirstCall()
throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Redirect the provider's output to a file
File urandom = new File(testDir, "urandom");
urandom.delete();
if (urandom.exists()) assertTrue(urandom.delete());
assertTrue(urandom.createNewFile());
assertEquals(0, urandom.length());
LinuxSecureRandomProvider p = new LinuxSecureRandomProvider(urandom);
UnixSecureRandomProvider p = new UnixSecureRandomProvider(urandom);
// Getting a provider should write entropy to the file
Provider provider = p.getProvider();
assertNotNull(provider);
assertEquals("LinuxPRNG", provider.getName());
assertEquals("UnixPRNG", provider.getName());
// There should be at least 16 bytes from the clock, 8 from the runtime
long length = urandom.length();
assertTrue(length >= 24);
// Getting another provider should not write to the file again
provider = p.getProvider();
assertNotNull(provider);
assertEquals("LinuxPRNG", provider.getName());
assertEquals("UnixPRNG", provider.getName());
assertEquals(length, urandom.length());
}
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
deleteTestDirectory(testDir);
}
}

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.OsUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -15,31 +14,33 @@ import java.io.FileOutputStream;
import java.util.HashSet;
import java.util.Set;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.util.OsUtils.isLinux;
import static org.briarproject.bramble.util.OsUtils.isMac;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
public class LinuxSecureRandomSpiTest extends BrambleTestCase {
public class UnixSecureRandomSpiTest extends BrambleTestCase {
private static final File RANDOM_DEVICE = new File("/dev/urandom");
private static final int SEED_BYTES = 32;
private final File testDir = TestUtils.getTestDirectory();
private final File testDir = getTestDirectory();
@Before
public void setUp() {
testDir.mkdirs();
assumeTrue(isLinux() || isMac());
assertTrue(testDir.mkdirs());
}
@Test
public void testSeedsAreDistinct() {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
Set<Bytes> seeds = new HashSet<>();
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi();
UnixSecureRandomSpi engine = new UnixSecureRandomSpi();
for (int i = 0; i < 1000; i++) {
byte[] seed = engine.engineGenerateSeed(SEED_BYTES);
assertEquals(SEED_BYTES, seed.length);
@@ -49,19 +50,15 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
@Test
public void testEngineSetSeedWritesToRandomDevice() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Redirect the engine's output to a file
File urandom = new File(testDir, "urandom");
urandom.delete();
if (urandom.exists()) assertTrue(urandom.delete());
assertTrue(urandom.createNewFile());
assertEquals(0, urandom.length());
// Generate a seed
byte[] seed = TestUtils.getRandomBytes(SEED_BYTES);
// Check that the engine writes the seed to the file
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(RANDOM_DEVICE,
UnixSecureRandomSpi engine = new UnixSecureRandomSpi(RANDOM_DEVICE,
urandom);
engine.engineSetSeed(seed);
assertEquals(SEED_BYTES, urandom.length());
@@ -74,15 +71,11 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
@Test
public void testEngineNextBytesReadsFromRandomDevice() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Generate some entropy
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
// Write the entropy to a file
File urandom = new File(testDir, "urandom");
urandom.delete();
if (urandom.exists()) assertTrue(urandom.delete());
FileOutputStream out = new FileOutputStream(urandom);
out.write(entropy);
out.flush();
@@ -90,7 +83,7 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
assertTrue(urandom.exists());
assertEquals(SEED_BYTES, urandom.length());
// Check that the engine reads from the file
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
UnixSecureRandomSpi engine = new UnixSecureRandomSpi(urandom,
RANDOM_DEVICE);
byte[] b = new byte[SEED_BYTES];
engine.engineNextBytes(b);
@@ -99,15 +92,11 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
@Test
public void testEngineGenerateSeedReadsFromRandomDevice() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Generate some entropy
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
// Write the entropy to a file
File urandom = new File(testDir, "urandom");
urandom.delete();
if (urandom.exists()) assertTrue(urandom.delete());
FileOutputStream out = new FileOutputStream(urandom);
out.write(entropy);
out.flush();
@@ -115,7 +104,7 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
assertTrue(urandom.exists());
assertEquals(SEED_BYTES, urandom.length());
// Check that the engine reads from the file
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
UnixSecureRandomSpi engine = new UnixSecureRandomSpi(urandom,
RANDOM_DEVICE);
byte[] b = engine.engineGenerateSeed(SEED_BYTES);
assertArrayEquals(entropy, b);
@@ -123,6 +112,6 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
deleteTestDirectory(testDir);
}
}

View File

@@ -4,6 +4,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.tree.TreeHash;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
@@ -27,4 +29,10 @@ public class TestMessageFactory implements MessageFactory {
System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length);
return raw;
}
@Override
public MessageId getMessageId(GroupId g, long timestamp,
TreeHash rootHash) {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,20 +1,21 @@
package org.briarproject.bramble.lifecycle;
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.util.OsUtils;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static org.briarproject.bramble.util.OsUtils.isWindows;
@Module
public class DesktopLifecycleModule extends LifecycleModule {
@Provides
@Singleton
ShutdownManager provideDesktopShutdownManager() {
if (OsUtils.isWindows()) return new WindowsShutdownManagerImpl();
if (isWindows()) return new WindowsShutdownManagerImpl();
else return new ShutdownManagerImpl();
}
}

View File

@@ -15,7 +15,6 @@ import com.sun.jna.win32.W32APIFunctionMapper;
import com.sun.jna.win32.W32APITypeMapper;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.OsUtils;
import java.util.Collections;
import java.util.HashMap;
@@ -29,6 +28,7 @@ import static com.sun.jna.Library.OPTION_FUNCTION_MAPPER;
import static com.sun.jna.Library.OPTION_TYPE_MAPPER;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.OsUtils.isWindows;
@ThreadSafe
@NotNullByDefault
@@ -71,7 +71,7 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
// Locking: lock
private void initialise() {
if (OsUtils.isWindows()) {
if (isWindows()) {
new EventLoop().start();
} else {
LOG.warning("Windows shutdown manager used on non-Windows OS");
@@ -111,7 +111,7 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
public void run() {
try {
// Load user32.dll
User32 user32 = (User32) Native.loadLibrary("user32",
User32 user32 = Native.loadLibrary("user32",
User32.class, options);
// Create a callback to handle the WM_QUERYENDSESSION message
WindowProc proc = (hwnd, msg, wp, lp) -> {

View File

@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.CodeSource;
@@ -19,9 +18,9 @@ import java.util.concurrent.Executor;
import javax.net.SocketFactory;
@NotNullByDefault
class LinuxTorPlugin extends TorPlugin {
abstract class JavaTorPlugin extends TorPlugin {
LinuxTorPlugin(Executor ioExecutor, NetworkManager networkManager,
JavaTorPlugin(Executor ioExecutor, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory,
Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
@@ -34,17 +33,6 @@ class LinuxTorPlugin extends TorPlugin {
torDirectory);
}
@Override
protected int getProcessId() {
try {
// Java 9: ProcessHandle.current().pid()
return Integer.parseInt(
new File("/proc/self").getCanonicalFile().getName());
} catch (IOException e) {
throw new AssertionError(e);
}
}
@Override
protected long getLastUpdateTime() {
CodeSource codeSource =
@@ -58,5 +46,4 @@ class LinuxTorPlugin extends TorPlugin {
throw new AssertionError(e);
}
}
}

View File

@@ -0,0 +1,47 @@
package org.briarproject.bramble.plugin.tor;
import com.sun.jna.Library;
import com.sun.jna.Native;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import java.io.File;
import java.util.concurrent.Executor;
import javax.net.SocketFactory;
@NotNullByDefault
class UnixTorPlugin extends JavaTorPlugin {
UnixTorPlugin(Executor ioExecutor, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory,
Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff,
DuplexPluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, File torDirectory) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
backoff, callback, architecture, maxLatency, maxIdleTime,
torDirectory);
}
@Override
protected int getProcessId() {
return CLibrary.INSTANCE.getpid();
}
private interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary) Native.loadLibrary("c", CLibrary.class);
int getpid();
}
}

View File

@@ -22,14 +22,15 @@ import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import javax.net.SocketFactory;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.OsUtils.isLinux;
@Immutable
@NotNullByDefault
public class LinuxTorPluginFactory implements DuplexPluginFactory {
public class UnixTorPluginFactory implements DuplexPluginFactory {
private static final Logger LOG =
Logger.getLogger(LinuxTorPluginFactory.class.getName());
getLogger(UnixTorPluginFactory.class.getName());
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
@@ -49,7 +50,7 @@ public class LinuxTorPluginFactory implements DuplexPluginFactory {
private final Clock clock;
private final File torDirectory;
public LinuxTorPluginFactory(Executor ioExecutor,
public UnixTorPluginFactory(Executor ioExecutor,
NetworkManager networkManager, LocationUtils locationUtils,
EventBus eventBus, SocketFactory torSocketFactory,
BackoffFactory backoffFactory, ResourceProvider resourceProvider,
@@ -95,7 +96,7 @@ public class LinuxTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
LinuxTorPlugin plugin = new LinuxTorPlugin(ioExecutor, networkManager,
UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, networkManager,
locationUtils, torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, backoff, callback,
architecture, MAX_LATENCY, MAX_IDLE_TIME, torDirectory);

View File

@@ -1,19 +1,24 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.util.OsUtils;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static org.briarproject.bramble.util.OsUtils.isLinux;
import static org.briarproject.bramble.util.OsUtils.isMac;
@Module
public class DesktopSecureRandomModule {
@Provides
@Singleton
SecureRandomProvider provideSecureRandomProvider() {
return OsUtils.isLinux() ? new LinuxSecureRandomProvider() : null;
if (isLinux() || isMac())
return new UnixSecureRandomProvider();
// TODO: Create a secure random provider for Windows
throw new UnsupportedOperationException();
}
}

View File

@@ -70,7 +70,7 @@ public class BridgeTest extends BrambleTestCase {
private final File torDir = getTestDirectory();
private final String bridge;
private LinuxTorPluginFactory factory;
private UnixTorPluginFactory factory;
public BridgeTest(String bridge) {
this.bridge = bridge;
@@ -108,7 +108,7 @@ public class BridgeTest extends BrambleTestCase {
return singletonList(bridge);
}
};
factory = new LinuxTorPluginFactory(ioExecutor, networkManager,
factory = new UnixTorPluginFactory(ioExecutor, networkManager,
locationUtils, eventBus, torSocketFactory, backoffFactory,
resourceProvider, bridgeProvider, batteryManager, clock,
torDir);
@@ -124,7 +124,7 @@ public class BridgeTest extends BrambleTestCase {
DuplexPlugin duplexPlugin =
factory.createPlugin(new TorPluginCallBack());
assertNotNull(duplexPlugin);
LinuxTorPlugin plugin = (LinuxTorPlugin) duplexPlugin;
UnixTorPlugin plugin = (UnixTorPlugin) duplexPlugin;
LOG.warning("Testing " + bridge);
try {

View File

@@ -104,6 +104,7 @@ dependencies {
}
implementation "com.android.support:cardview-v7:$supportVersion"
implementation "com.android.support:support-annotations:$supportVersion"
implementation "com.android.support:exifinterface:$supportVersion"
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "android.arch.lifecycle:extensions:1.1.1"
@@ -117,8 +118,15 @@ dependencies {
implementation 'com.google.zxing:core:3.3.3'
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.12.4'
implementation 'com.vanniktech:emoji-google:0.5.1'
def glideVersion = '4.8.0'
implementation("com.github.bumptech.glide:glide:$glideVersion") {
exclude group: 'com.android.support'
exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it
}
implementation 'com.github.chrisbanes:PhotoView:2.1.4' // later versions already use androidx
annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
compileOnly 'javax.annotation:jsr250-api:1.0'

View File

@@ -30,3 +30,6 @@
# Emoji
-keep class com.vanniktech.emoji.**
# Glide
-dontwarn com.bumptech.glide.load.engine.cache.DiskLruCacheWrapper

View File

@@ -1,5 +1,6 @@
package org.briarproject.briar.android;
import org.briarproject.bramble.BrambleAndroidModule;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.briar.BriarCoreModule;
@@ -12,6 +13,7 @@ public class BriarTestComponentApplication extends BriarApplicationImpl {
// We need to load the eager singletons directly after making the
// dependency graphs
BrambleCoreModule.initEagerSingletons(component);
BrambleAndroidModule.initEagerSingletons(component);
BriarCoreModule.initEagerSingletons(component);
AndroidEagerSingletons.initEagerSingletons(component);
return component;

View File

@@ -7,7 +7,6 @@
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
@@ -18,6 +17,7 @@
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission-sdk-23 android:name="android.permission.USE_BIOMETRIC" />
@@ -113,6 +113,15 @@
/>
</activity>
<activity
android:name=".android.conversation.ImageActivity"
android:parentActivityName="org.briarproject.briar.android.conversation.ConversationActivity"
android:theme="@style/BriarTheme.Transparent.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.conversation.ConversationActivity"/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity"
android:label="@string/groups_create_group_title"

View File

@@ -2,6 +2,7 @@ package org.briarproject.briar.android;
import android.arch.lifecycle.ViewModelProvider;
import org.briarproject.bramble.BrambleAndroidEagerSingletons;
import org.briarproject.bramble.BrambleAndroidModule;
import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule;
@@ -28,6 +29,7 @@ import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.briar.BriarCoreEagerSingletons;
import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
import org.briarproject.briar.android.login.SignInReminderReceiver;
import org.briarproject.briar.android.reporting.BriarReportSender;
import org.briarproject.briar.android.view.TextInputView;
@@ -39,11 +41,11 @@ import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostFactory;
import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.feed.FeedManager;
import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.introduction.IntroductionManager;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.api.privategroup.GroupMessageFactory;
@@ -68,7 +70,8 @@ import dagger.Component;
AppModule.class
})
public interface AndroidComponent
extends BrambleCoreEagerSingletons, BriarCoreEagerSingletons {
extends BrambleCoreEagerSingletons, BrambleAndroidEagerSingletons,
BriarCoreEagerSingletons {
// Exposed objects
@CryptoExecutor
@@ -168,6 +171,8 @@ public interface AndroidComponent
void inject(TextInputView textInputView);
void inject(BriarModelLoader briarModelLoader);
// Eager singleton load
void inject(AppModule.EagerSingletons init);
}

View File

@@ -66,10 +66,14 @@ public class AppModule {
@Inject
AndroidNotificationManager androidNotificationManager;
@Inject
ScreenFilterMonitor screenFilterMonitor;
@Inject
NetworkUsageLogger networkUsageLogger;
@Inject
DozeWatchdog dozeWatchdog;
@Inject
LockManager lockManager;
@Inject
RecentEmoji recentEmoji;
}

View File

@@ -15,6 +15,7 @@ import com.vanniktech.emoji.google.GoogleEmojiProvider;
import org.acra.ACRA;
import org.acra.ReportingInteractionMode;
import org.acra.annotation.ReportsCrashes;
import org.briarproject.bramble.BrambleAndroidModule;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.R;
@@ -124,6 +125,7 @@ public class BriarApplicationImpl extends Application
// We need to load the eager singletons directly after making the
// dependency graphs
BrambleCoreModule.initEagerSingletons(androidComponent);
BrambleAndroidModule.initEagerSingletons(androidComponent);
BriarCoreModule.initEagerSingletons(androidComponent);
AndroidEagerSingletons.initEagerSingletons(androidComponent);
return androidComponent;

View File

@@ -15,10 +15,11 @@ import org.briarproject.briar.android.blog.ReblogFragment;
import org.briarproject.briar.android.blog.RssFeedImportActivity;
import org.briarproject.briar.android.blog.RssFeedManageActivity;
import org.briarproject.briar.android.blog.WriteBlogPostActivity;
import org.briarproject.briar.android.conversation.AliasDialogFragment;
import org.briarproject.briar.android.contact.ContactListFragment;
import org.briarproject.briar.android.contact.ContactModule;
import org.briarproject.briar.android.conversation.AliasDialogFragment;
import org.briarproject.briar.android.conversation.ConversationActivity;
import org.briarproject.briar.android.conversation.ImageActivity;
import org.briarproject.briar.android.forum.CreateForumActivity;
import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.forum.ForumListFragment;
@@ -110,6 +111,8 @@ public interface ActivityComponent {
void inject(ConversationActivity activity);
void inject(ImageActivity activity);
void inject(ForumInvitationActivity activity);
void inject(BlogInvitationActivity activity);

View File

@@ -2,12 +2,11 @@ package org.briarproject.briar.android.activity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.support.annotation.RequiresApi;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.transition.Slide;
import android.transition.Transition;
import android.view.Gravity;
import android.view.Window;
import android.widget.CheckBox;
@@ -33,6 +32,7 @@ import static android.os.Build.VERSION.SDK_INT;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_UNLOCK;
import static org.briarproject.briar.android.util.UiUtils.excludeSystemUi;
import static org.briarproject.briar.android.util.UiUtils.getDozeWhitelistingIntent;
import static org.briarproject.briar.android.util.UiUtils.isSamsung7;
@@ -111,21 +111,28 @@ public abstract class BriarActivity extends BaseActivity {
lockManager.onActivityStop();
}
public void setSceneTransitionAnimation() {
if (SDK_INT < 21) return;
/**
* Sets the transition animations.
* @param enterTransition used to move views into initial positions
* @param exitTransition used to move views out when starting a <b>new</b> activity.
* @param returnTransition used when window is closing, because the activity is finishing.
*/
@RequiresApi(api = 21)
public void setSceneTransitionAnimation(
@Nullable Transition enterTransition,
@Nullable Transition exitTransition,
@Nullable Transition returnTransition) {
// workaround for #1007
if (isSamsung7()) {
return;
}
Transition slide = new Slide(Gravity.RIGHT);
slide.excludeTarget(android.R.id.statusBarBackground, true);
slide.excludeTarget(android.R.id.navigationBarBackground, true);
if (enterTransition != null) excludeSystemUi(enterTransition);
if (exitTransition != null) excludeSystemUi(exitTransition);
if (returnTransition != null) excludeSystemUi(returnTransition);
Window window = getWindow();
window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
window.setExitTransition(slide);
window.setEnterTransition(slide);
window.setTransitionBackgroundFadeDuration(getResources()
.getInteger(android.R.integer.config_longAnimTime));
window.setEnterTransition(enterTransition);
window.setExitTransition(exitTransition);
window.setReturnTransition(returnTransition);
}
/**

View File

@@ -18,7 +18,6 @@ public class ReblogActivity extends BriarActivity implements
@Override
public void onCreate(Bundle savedInstanceState) {
setSceneTransitionAnimation();
super.onCreate(savedInstanceState);
Intent intent = getIntent();

View File

@@ -0,0 +1,209 @@
package org.briarproject.briar.android.conversation;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.support.annotation.Nullable;
import android.support.media.ExifInterface;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.api.messaging.Attachment;
import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.MessagingManager;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import static android.support.media.ExifInterface.ORIENTATION_ROTATE_270;
import static android.support.media.ExifInterface.ORIENTATION_ROTATE_90;
import static android.support.media.ExifInterface.ORIENTATION_TRANSPOSE;
import static android.support.media.ExifInterface.ORIENTATION_TRANSVERSE;
import static android.support.media.ExifInterface.TAG_IMAGE_LENGTH;
import static android.support.media.ExifInterface.TAG_IMAGE_WIDTH;
import static android.support.media.ExifInterface.TAG_ORIENTATION;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
@NotNullByDefault
class AttachmentController {
private static final Logger LOG =
getLogger(AttachmentController.class.getName());
private final MessagingManager messagingManager;
private final int defaultSize;
private final int minWidth, maxWidth;
private final int minHeight, maxHeight;
private final Map<MessageId, List<AttachmentItem>> attachmentCache =
new ConcurrentHashMap<>();
AttachmentController(MessagingManager messagingManager, Resources res) {
this.messagingManager = messagingManager;
defaultSize =
res.getDimensionPixelSize(R.dimen.message_bubble_image_default);
minWidth = res.getDimensionPixelSize(
R.dimen.message_bubble_image_min_width);
maxWidth = res.getDimensionPixelSize(
R.dimen.message_bubble_image_max_width);
minHeight = res.getDimensionPixelSize(
R.dimen.message_bubble_image_min_height);
maxHeight = res.getDimensionPixelSize(
R.dimen.message_bubble_image_max_height);
}
void put(MessageId messageId, List<AttachmentItem> attachments) {
attachmentCache.put(messageId, attachments);
}
@Nullable
List<AttachmentItem> get(MessageId messageId) {
return attachmentCache.get(messageId);
}
@DatabaseExecutor
List<Pair<AttachmentHeader, Attachment>> getMessageAttachments(
List<AttachmentHeader> headers) throws DbException {
long start = now();
List<Pair<AttachmentHeader, Attachment>> attachments =
new ArrayList<>(headers.size());
for (AttachmentHeader h : headers) {
Attachment a =
messagingManager.getAttachment(h.getMessageId());
attachments.add(new Pair<>(h, a));
}
logDuration(LOG, "Loading attachment", start);
return attachments;
}
List<AttachmentItem> getAttachmentItems(
List<Pair<AttachmentHeader, Attachment>> attachments) {
List<AttachmentItem> items = new ArrayList<>(attachments.size());
for (Pair<AttachmentHeader, Attachment> a : attachments) {
AttachmentItem item =
getAttachmentItem(a.getFirst(), a.getSecond());
items.add(item);
}
return items;
}
private AttachmentItem getAttachmentItem(AttachmentHeader h, Attachment a) {
MessageId messageId = h.getMessageId();
Size size = new Size();
InputStream is = a.getStream();
is.mark(Integer.MAX_VALUE);
try {
// use exif to get size
if (h.getContentType().equals("image/jpeg")) {
size = getSizeFromExif(is);
}
} catch (IOException e) {
logException(LOG, WARNING, e);
}
try {
// use BitmapFactory to get size
if (size.error) {
is.reset();
size = getSizeFromBitmap(is);
}
} catch (IOException e) {
logException(LOG, WARNING, e);
} finally {
tryToClose(is, LOG, WARNING);
}
// calculate thumbnail size
Size thumbnailSize = new Size(defaultSize, defaultSize);
if (!size.error) {
thumbnailSize = getThumbnailSize(size.width, size.height);
}
return new AttachmentItem(messageId, size.width, size.height,
thumbnailSize.width, thumbnailSize.height, size.error);
}
/**
* Gets the size of a JPEG {@link InputStream} if EXIF info is available.
*/
private static Size getSizeFromExif(InputStream is)
throws IOException {
ExifInterface exif = new ExifInterface(is);
// these can return 0 independent of default value
int width = exif.getAttributeInt(TAG_IMAGE_WIDTH, 0);
int height = exif.getAttributeInt(TAG_IMAGE_LENGTH, 0);
if (width == 0 || height == 0) return new Size();
int orientation = exif.getAttributeInt(TAG_ORIENTATION, 0);
if (orientation == ORIENTATION_ROTATE_90 ||
orientation == ORIENTATION_ROTATE_270 ||
orientation == ORIENTATION_TRANSVERSE ||
orientation == ORIENTATION_TRANSPOSE) {
//noinspection SuspiciousNameCombination
return new Size(height, width);
}
return new Size(width, height);
}
/**
* Gets the size of any image {@link InputStream}.
*/
private static Size getSizeFromBitmap(InputStream is) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
if (options.outWidth < 1 || options.outHeight < 1)
return new Size();
return new Size(options.outWidth, options.outHeight);
}
private Size getThumbnailSize(int width, int height) {
float widthPercentage = maxWidth / (float) width;
float heightPercentage = maxHeight / (float) height;
float scaleFactor = Math.min(widthPercentage, heightPercentage);
if (scaleFactor > 1) scaleFactor = 1f;
int thumbnailWidth = (int) (width * scaleFactor);
int thumbnailHeight = (int) (height * scaleFactor);
if (thumbnailWidth < minWidth || thumbnailHeight < minHeight) {
widthPercentage = minWidth / (float) width;
heightPercentage = minHeight / (float) height;
scaleFactor = Math.max(widthPercentage, heightPercentage);
thumbnailWidth = (int) (width * scaleFactor);
thumbnailHeight = (int) (height * scaleFactor);
if (thumbnailWidth > maxWidth) thumbnailWidth = maxWidth;
if (thumbnailHeight > maxHeight) thumbnailHeight = maxHeight;
}
return new Size(thumbnailWidth, thumbnailHeight);
}
private static class Size {
private final int width;
private final int height;
private final boolean error;
private Size(int width, int height) {
this.width = width;
this.height = height;
this.error = false;
}
private Size() {
this.width = 0;
this.height = 0;
this.error = true;
}
}
}

View File

@@ -0,0 +1,98 @@
package org.briarproject.briar.android.conversation;
import android.os.Parcel;
import android.os.Parcelable;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class AttachmentItem implements Parcelable {
private final MessageId messageId;
private final int width, height;
private final int thumbnailWidth, thumbnailHeight;
private final boolean hasError;
public static final Creator<AttachmentItem> CREATOR =
new Creator<AttachmentItem>() {
@Override
public AttachmentItem createFromParcel(Parcel in) {
return new AttachmentItem(in);
}
@Override
public AttachmentItem[] newArray(int size) {
return new AttachmentItem[size];
}
};
AttachmentItem(MessageId messageId, int width, int height,
int thumbnailWidth, int thumbnailHeight, boolean hasError) {
this.messageId = messageId;
this.width = width;
this.height = height;
this.thumbnailWidth = thumbnailWidth;
this.thumbnailHeight = thumbnailHeight;
this.hasError = hasError;
}
protected AttachmentItem(Parcel in) {
byte[] messageIdByte = new byte[MessageId.LENGTH];
in.readByteArray(messageIdByte);
messageId = new MessageId(messageIdByte);
width = in.readInt();
height = in.readInt();
thumbnailWidth = in.readInt();
thumbnailHeight = in.readInt();
hasError = in.readByte() != 0;
}
public MessageId getMessageId() {
return messageId;
}
int getWidth() {
return width;
}
int getHeight() {
return height;
}
int getThumbnailWidth() {
return thumbnailWidth;
}
int getThumbnailHeight() {
return thumbnailHeight;
}
boolean hasError() {
return hasError;
}
// TODO use counter instead, because in theory one attachment can appear in more than one messages
String getTransitionName() {
return String.valueOf(messageId.hashCode());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByteArray(messageId.getBytes());
dest.writeInt(width);
dest.writeInt(height);
dest.writeInt(thumbnailWidth);
dest.writeInt(thumbnailHeight);
dest.writeByte((byte) (hasError ? 1 : 0));
}
}

View File

@@ -9,11 +9,15 @@ import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.Toolbar;
import android.transition.Slide;
import android.transition.Transition;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
@@ -24,6 +28,7 @@ import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
@@ -51,6 +56,7 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.blog.BlogActivity;
import org.briarproject.briar.android.conversation.ConversationVisitor.AttachmentCache;
import org.briarproject.briar.android.conversation.ConversationVisitor.TextCache;
import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.introduction.IntroductionActivity;
@@ -69,6 +75,8 @@ import org.briarproject.briar.api.conversation.ConversationResponse;
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.introduction.IntroductionManager;
import org.briarproject.briar.api.messaging.Attachment;
import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
@@ -92,10 +100,14 @@ import im.delight.android.identicons.IdenticonDrawable;
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.PromptStateChangeListener;
import static android.os.Build.VERSION.SDK_INT;
import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
import static android.support.v4.view.ViewCompat.setTransitionName;
import static android.support.v7.util.SortedList.INVALID_POSITION;
import static android.view.Gravity.END;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.Collections.emptyList;
import static java.util.Collections.sort;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@@ -103,6 +115,9 @@ import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION;
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT;
import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
@@ -115,7 +130,7 @@ import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.S
@ParametersNotNullByDefault
public class ConversationActivity extends BriarActivity
implements EventListener, ConversationListener, TextInputListener,
TextCache {
TextCache, AttachmentCache {
public static final String CONTACT_ID = "briar.CONTACT_ID";
@@ -133,6 +148,7 @@ public class ConversationActivity extends BriarActivity
Executor cryptoExecutor;
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
private AttachmentController attachmentController;
private ConversationViewModel viewModel;
private ConversationVisitor visitor;
@@ -142,6 +158,7 @@ public class ConversationActivity extends BriarActivity
private ImageView toolbarStatus;
private TextView toolbarTitle;
private BriarRecyclerView list;
private LinearLayoutManager layoutManager;
private TextInputView textInputView;
// Fields that are accessed from background threads must be volatile
@@ -179,7 +196,10 @@ public class ConversationActivity extends BriarActivity
@Override
public void onCreate(@Nullable Bundle state) {
setSceneTransitionAnimation();
if (SDK_INT >= 21) {
Transition slide = new Slide(END);
setSceneTransitionAnimation(slide, null, slide);
}
super.onCreate(state);
Intent i = getIntent();
@@ -190,6 +210,7 @@ public class ConversationActivity extends BriarActivity
viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(ConversationViewModel.class);
viewModel.setContactId(contactId);
attachmentController = viewModel.getAttachmentController();
setContentView(R.layout.activity_conversation);
@@ -216,11 +237,12 @@ public class ConversationActivity extends BriarActivity
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
visitor = new ConversationVisitor(this, this,
visitor = new ConversationVisitor(this, this, this,
viewModel.getContactDisplayName());
adapter = new ConversationAdapter(this, this);
list = findViewById(R.id.conversationView);
list.setLayoutManager(new LinearLayoutManager(this));
layoutManager = new LinearLayoutManager(this);
list.setLayoutManager(layoutManager);
list.setAdapter(adapter);
list.setEmptyText(getString(R.string.no_private_messages));
@@ -330,7 +352,43 @@ public class ConversationActivity extends BriarActivity
Collection<ConversationMessageHeader> headers =
conversationManager.getMessageHeaders(contactId);
logDuration(LOG, "Loading messages", start);
displayMessages(revision, headers);
// Sort headers by timestamp in *descending* order
List<ConversationMessageHeader> sorted =
new ArrayList<>(headers);
sort(sorted, (a, b) ->
Long.compare(b.getTimestamp(), a.getTimestamp()));
if (!sorted.isEmpty()) {
// If the latest header is a private message, eagerly load
// its text so we can set the scroll position correctly
ConversationMessageHeader latest = sorted.get(0);
if (latest instanceof PrivateMessageHeader) {
MessageId id = latest.getId();
PrivateMessageHeader h = (PrivateMessageHeader) latest;
if (h.hasText()) {
String text = textCache.get(id);
if (text == null) {
LOG.info(
"Eagerly loading text of latest message");
text = messagingManager.getMessageText(id);
textCache.put(id, text);
}
}
if (!h.getAttachmentHeaders().isEmpty()) {
List<AttachmentItem> items =
attachmentController.get(id);
if (items == null) {
LOG.info(
"Eagerly loading image size for latest message");
items = attachmentController.getAttachmentItems(
attachmentController
.getMessageAttachments(
h.getAttachmentHeaders()));
attachmentController.put(id, items);
}
}
}
}
displayMessages(revision, sorted);
} catch (NoSuchContactException e) {
finishOnUiThread();
} catch (DbException e) {
@@ -387,16 +445,44 @@ public class ConversationActivity extends BriarActivity
private void displayMessageText(MessageId m, String text) {
runOnUiThreadUnlessDestroyed(() -> {
textCache.put(m, text);
SparseArray<ConversationMessageItem> messages =
adapter.getMessageItems();
for (int i = 0; i < messages.size(); i++) {
ConversationItem item = messages.valueAt(i);
if (item.getId().equals(m)) {
item.setText(text);
adapter.notifyItemChanged(messages.keyAt(i));
list.scrollToPosition(adapter.getItemCount() - 1);
return;
}
Pair<Integer, ConversationMessageItem> pair =
adapter.getMessageItem(m);
if (pair != null) {
pair.getSecond().setText(text);
boolean bottom = adapter.isScrolledToBottom(layoutManager);
adapter.notifyItemChanged(pair.getFirst());
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
}
});
}
private void loadMessageAttachments(MessageId messageId,
List<AttachmentHeader> headers) {
runOnDbThread(() -> {
try {
List<Pair<AttachmentHeader, Attachment>> attachments =
attachmentController.getMessageAttachments(headers);
// TODO move getting the items off to the IoExecutor
List<AttachmentItem> items =
attachmentController.getAttachmentItems(attachments);
displayMessageAttachments(messageId, items);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void displayMessageAttachments(MessageId m,
List<AttachmentItem> items) {
runOnUiThreadUnlessDestroyed(() -> {
attachmentController.put(m, items);
Pair<Integer, ConversationMessageItem> pair =
adapter.getMessageItem(m);
if (pair != null) {
pair.getSecond().setAttachments(items);
boolean bottom = adapter.isScrolledToBottom(layoutManager);
adapter.notifyItemChanged(pair.getFirst());
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
}
});
}
@@ -445,10 +531,10 @@ public class ConversationActivity extends BriarActivity
private void addConversationItem(ConversationItem item) {
runOnUiThreadUnlessDestroyed(() -> {
boolean bottom = adapter.isScrolledToBottom(layoutManager);
adapter.incrementRevision();
adapter.add(item);
// Scroll to the bottom
list.scrollToPosition(adapter.getItemCount() - 1);
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
});
}
@@ -729,6 +815,31 @@ public class ConversationActivity extends BriarActivity
startActivity(i);
}
@Override
public void onAttachmentClicked(View view,
ConversationMessageItem messageItem, AttachmentItem item) {
String name;
if (messageItem.isIncoming()) {
// must be available when items are being displayed
name = viewModel.getContactDisplayName().getValue();
} else {
name = getString(R.string.you);
}
Intent i = new Intent(this, ImageActivity.class);
i.putExtra(ATTACHMENT, item);
i.putExtra(NAME, name);
i.putExtra(DATE, messageItem.getTime());
if (SDK_INT >= 23) {
String transitionName = item.getTransitionName();
ActivityOptionsCompat options =
makeSceneTransitionAnimation(this, view, transitionName);
ActivityCompat.startActivity(this, i, options.toBundle());
} else {
// work-around for android bug #224270
startActivity(i);
}
}
@DatabaseExecutor
private void respondToIntroductionRequest(SessionId sessionId,
boolean accept, long time) throws DbException {
@@ -761,4 +872,16 @@ public class ConversationActivity extends BriarActivity
if (text == null) loadMessageText(m);
return text;
}
@Override
public List<AttachmentItem> getAttachmentItems(MessageId m,
List<AttachmentHeader> headers) {
List<AttachmentItem> attachments = attachmentController.get(m);
if (attachments == null) {
loadMessageAttachments(m, headers);
return emptyList();
}
return attachments;
}
}

View File

@@ -2,12 +2,15 @@ package org.briarproject.briar.android.conversation;
import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.v7.widget.LinearLayoutManager;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.android.util.BriarAdapter;
@@ -98,16 +101,20 @@ class ConversationAdapter
return messages;
}
SparseArray<ConversationMessageItem> getMessageItems() {
SparseArray<ConversationMessageItem> messages = new SparseArray<>();
@Nullable
Pair<Integer, ConversationMessageItem> getMessageItem(MessageId messageId) {
for (int i = 0; i < items.size(); i++) {
ConversationItem item = items.get(i);
if (item instanceof ConversationMessageItem) {
messages.put(i, (ConversationMessageItem) item);
if (item instanceof ConversationMessageItem &&
item.getId().equals(messageId)) {
return new Pair<>(i, (ConversationMessageItem) item);
}
}
return messages;
return null;
}
boolean isScrolledToBottom(LinearLayoutManager layoutManager) {
return layoutManager.findLastVisibleItemPosition() == items.size() - 1;
}
}

View File

@@ -3,9 +3,9 @@ package org.briarproject.briar.android.conversation;
import android.support.annotation.CallSuper;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.constraint.ConstraintLayout;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -18,11 +18,11 @@ import static org.briarproject.briar.android.util.UiUtils.formatDate;
@NotNullByDefault
abstract class ConversationItemViewHolder extends ViewHolder {
protected final ViewGroup layout;
protected final ConstraintLayout layout;
@Nullable
private final OutItemViewHolder outViewHolder;
private final TextView text;
private final TextView time;
protected final TextView time;
ConversationItemViewHolder(View v, boolean isIncoming) {
super(v);

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.conversation;
import android.support.annotation.UiThread;
import android.view.View;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -14,4 +15,7 @@ interface ConversationListener {
void openRequestedShareable(ConversationRequestItem item);
void onAttachmentClicked(View view, ConversationMessageItem messageItem,
AttachmentItem attachmentItem);
}

View File

@@ -3,7 +3,6 @@ package org.briarproject.briar.android.conversation;
import android.support.annotation.LayoutRes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import java.util.List;
@@ -14,15 +13,20 @@ import javax.annotation.concurrent.NotThreadSafe;
@NotNullByDefault
class ConversationMessageItem extends ConversationItem {
private final List<AttachmentHeader> attachments;
private List<AttachmentItem> attachments;
ConversationMessageItem(@LayoutRes int layoutRes, PrivateMessageHeader h) {
ConversationMessageItem(@LayoutRes int layoutRes, PrivateMessageHeader h,
List<AttachmentItem> attachments) {
super(layoutRes, h);
this.attachments = h.getAttachmentHeaders();
this.attachments = attachments;
}
List<AttachmentHeader> getAttachments() {
List<AttachmentItem> getAttachments() {
return attachments;
}
void setAttachments(List<AttachmentItem> attachments) {
this.attachments = attachments;
}
}

View File

@@ -1,18 +1,161 @@
package org.briarproject.briar.android.conversation;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.support.annotation.DrawableRes;
import android.support.annotation.UiThread;
import android.support.constraint.ConstraintSet;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.bumptech.glide.load.Transformation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.conversation.glide.BriarImageTransformation;
import org.briarproject.briar.android.conversation.glide.GlideApp;
import static android.os.Build.VERSION.SDK_INT;
import static android.support.v4.view.ViewCompat.LAYOUT_DIRECTION_RTL;
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
@UiThread
@NotNullByDefault
class ConversationMessageViewHolder extends ConversationItemViewHolder {
// image support will be added here (#1242)
@DrawableRes
private static final int ERROR_RES = R.drawable.ic_image_broken;
private final ImageView imageView;
private final ViewGroup statusLayout;
private final int timeColor, timeColorBubble;
private final int radiusBig, radiusSmall;
private final boolean isRtl;
private final ConstraintSet textConstraints = new ConstraintSet();
private final ConstraintSet imageConstraints = new ConstraintSet();
private final ConstraintSet imageTextConstraints = new ConstraintSet();
ConversationMessageViewHolder(View v, boolean isIncoming) {
super(v, isIncoming);
imageView = v.findViewById(R.id.imageView);
statusLayout = v.findViewById(R.id.statusLayout);
radiusBig = v.getContext().getResources()
.getDimensionPixelSize(R.dimen.message_bubble_radius_big);
radiusSmall = v.getContext().getResources()
.getDimensionPixelSize(R.dimen.message_bubble_radius_small);
// remember original status text color
timeColor = time.getCurrentTextColor();
timeColorBubble =
ContextCompat.getColor(v.getContext(), R.color.briar_white);
// find out if we are showing a RTL language, Use the configuration,
// because getting the layout direction of views is not reliable
Configuration config =
imageView.getContext().getResources().getConfiguration();
isRtl = SDK_INT >= 17 &&
config.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
// clone constraint sets from layout files
textConstraints
.clone(v.getContext(), R.layout.list_item_conversation_msg_in);
imageConstraints.clone(v.getContext(),
R.layout.list_item_conversation_msg_image);
imageTextConstraints.clone(v.getContext(),
R.layout.list_item_conversation_msg_image_text);
// in/out are different layouts, so we need to do this only once
textConstraints
.setHorizontalBias(R.id.statusLayout, isIncoming() ? 1 : 0);
imageConstraints
.setHorizontalBias(R.id.statusLayout, isIncoming() ? 1 : 0);
imageTextConstraints
.setHorizontalBias(R.id.statusLayout, isIncoming() ? 1 : 0);
}
@Override
void bind(ConversationItem conversationItem,
ConversationListener listener) {
super.bind(conversationItem, listener);
ConversationMessageItem item =
(ConversationMessageItem) conversationItem;
if (item.getAttachments().isEmpty()) {
bindTextItem();
} else {
bindImageItem(item, listener);
}
}
private void bindTextItem() {
clearImage();
statusLayout.setBackgroundResource(0);
// also reset padding (the background drawable defines some)
statusLayout.setPadding(0, 0, 0, 0);
time.setTextColor(timeColor);
textConstraints.applyTo(layout);
}
private void bindImageItem(ConversationMessageItem item,
ConversationListener listener) {
// TODO show more than just the first image
AttachmentItem attachment = item.getAttachments().get(0);
ConstraintSet constraintSet;
if (item.getText() == null) {
statusLayout
.setBackgroundResource(R.drawable.msg_status_bubble);
time.setTextColor(timeColorBubble);
constraintSet = imageConstraints;
} else {
statusLayout.setBackgroundResource(0);
// also reset padding (the background drawable defines some)
statusLayout.setPadding(0, 0, 0, 0);
time.setTextColor(timeColor);
constraintSet = imageTextConstraints;
}
// apply image size constraints, so glides picks them up for scaling
int width = attachment.getThumbnailWidth();
int height = attachment.getThumbnailHeight();
constraintSet.constrainWidth(R.id.imageView, width);
constraintSet.constrainHeight(R.id.imageView, height);
constraintSet.applyTo(layout);
if (attachment.hasError()) {
clearImage();
imageView.setImageResource(ERROR_RES);
} else {
loadImage(item, attachment, listener);
}
}
private void clearImage() {
GlideApp.with(imageView)
.clear(imageView);
imageView.setOnClickListener(null);
}
private void loadImage(ConversationMessageItem item,
AttachmentItem attachment, ConversationListener listener) {
boolean leftCornerSmall =
(isIncoming() && !isRtl) || (!isIncoming() && isRtl);
boolean bottomRound = item.getText() == null;
Transformation<Bitmap> transformation = new BriarImageTransformation(
radiusSmall, radiusBig, leftCornerSmall, bottomRound);
GlideApp.with(imageView)
.load(attachment)
.diskCacheStrategy(NONE)
.error(ERROR_RES)
.transform(transformation)
.transition(withCrossFade())
.into(imageView)
.waitForLayout();
imageView.setOnClickListener(
view -> listener.onAttachmentClicked(view, item, attachment));
}
}

View File

@@ -0,0 +1,19 @@
package org.briarproject.briar.android.conversation;
import org.briarproject.briar.android.activity.ActivityScope;
import org.briarproject.briar.android.conversation.glide.BriarDataFetcherFactory;
import dagger.Module;
import dagger.Provides;
@Module
public class ConversationModule {
@ActivityScope
@Provides
BriarDataFetcherFactory provideBriarDataFetcherFactory(
BriarDataFetcherFactory dataFetcherFactory) {
return dataFetcherFactory;
}
}

View File

@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.util.UiUtils;
import org.briarproject.briar.api.messaging.MessagingManager;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -38,6 +39,7 @@ public class ConversationViewModel extends AndroidViewModel {
@DatabaseExecutor
private final Executor dbExecutor;
private final ContactManager contactManager;
private final AttachmentController attachmentController;
@Nullable
private ContactId contactId = null;
@@ -52,10 +54,12 @@ public class ConversationViewModel extends AndroidViewModel {
@Inject
ConversationViewModel(Application application,
@DatabaseExecutor Executor dbExecutor,
ContactManager contactManager) {
ContactManager contactManager, MessagingManager messagingManager) {
super(application);
this.dbExecutor = dbExecutor;
this.contactManager = contactManager;
this.attachmentController = new AttachmentController(messagingManager,
application.getResources());
contactDeleted.setValue(false);
}
@@ -96,6 +100,10 @@ public class ConversationViewModel extends AndroidViewModel {
});
}
AttachmentController getAttachmentController() {
return attachmentController;
}
LiveData<Contact> getContact() {
return contact;
}

View File

@@ -9,17 +9,21 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.api.blog.BlogInvitationRequest;
import org.briarproject.briar.api.blog.BlogInvitationResponse;
import org.briarproject.briar.api.conversation.ConversationMessageVisitor;
import org.briarproject.briar.api.forum.ForumInvitationRequest;
import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.introduction.IntroductionRequest;
import org.briarproject.briar.api.introduction.IntroductionResponse;
import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.conversation.ConversationMessageVisitor;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse;
import java.util.List;
import javax.annotation.Nullable;
import static java.util.Collections.emptyList;
import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.BLOG;
import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.FORUM;
import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.GROUP;
@@ -33,24 +37,33 @@ class ConversationVisitor implements
private final Context ctx;
private final TextCache textCache;
private final AttachmentCache attachmentCache;
private final LiveData<String> contactName;
ConversationVisitor(Context ctx, TextCache textCache,
LiveData<String> contactName) {
AttachmentCache attachmentCache, LiveData<String> contactName) {
this.ctx = ctx;
this.textCache = textCache;
this.attachmentCache = attachmentCache;
this.contactName = contactName;
}
@Override
public ConversationItem visitPrivateMessageHeader(PrivateMessageHeader h) {
ConversationItem item;
List<AttachmentItem> attachments;
if (h.getAttachmentHeaders().isEmpty()) {
attachments = emptyList();
} else {
attachments = attachmentCache
.getAttachmentItems(h.getId(), h.getAttachmentHeaders());
}
if (h.isLocal()) {
item = new ConversationMessageItem(
R.layout.list_item_conversation_msg_out, h);
R.layout.list_item_conversation_msg_out, h, attachments);
} else {
item = new ConversationMessageItem(
R.layout.list_item_conversation_msg_in, h);
R.layout.list_item_conversation_msg_in, h, attachments);
}
if (h.hasText()) {
String text = textCache.getText(h.getId());
@@ -216,11 +229,18 @@ class ConversationVisitor implements
return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_out, text, r);
} else {
String text = ctx.getString(R.string.introduction_request_received,
contactName.getValue(), name);
String text;
if (r.isContact()) {
text = ctx.getString(
R.string.introduction_request_exists_received,
contactName.getValue(), name);
} else {
text = ctx.getString(R.string.introduction_request_received,
contactName.getValue(), name);
}
return new ConversationRequestItem(
R.layout.list_item_conversation_request, text, INTRODUCTION,
r);
R.layout.list_item_conversation_request, text,
INTRODUCTION, r);
}
}
@@ -272,4 +292,9 @@ class ConversationVisitor implements
@Nullable
String getText(MessageId m);
}
interface AttachmentCache {
List<AttachmentItem> getAttachmentItems(MessageId m,
List<AttachmentHeader> headers);
}
}

View File

@@ -0,0 +1,233 @@
package org.briarproject.briar.android.conversation;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.design.widget.AppBarLayout;
import android.support.v7.widget.Toolbar;
import android.transition.Fade;
import android.transition.Transition;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.github.chrisbanes.photoview.PhotoView;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.conversation.glide.GlideApp;
import org.briarproject.briar.android.view.PullDownLayout;
import static android.graphics.Color.TRANSPARENT;
import static android.os.Build.VERSION.SDK_INT;
import static android.view.View.GONE;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.widget.ImageView.ScaleType.FIT_START;
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute;
public class ImageActivity extends BriarActivity
implements PullDownLayout.Callback {
final static String ATTACHMENT = "attachment";
final static String NAME = "name";
final static String DATE = "date";
private PullDownLayout layout;
private AppBarLayout appBarLayout;
private PhotoView photoView;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
// Transitions
supportPostponeEnterTransition();
Window window = getWindow();
if (SDK_INT >= 21) {
Transition transition = new Fade();
setSceneTransitionAnimation(transition, null, transition);
}
// inflate layout
setContentView(R.layout.activity_image);
layout = findViewById(R.id.layout);
layout.getBackground().setAlpha(255);
layout.setCallback(this);
// Status Bar
if (SDK_INT >= 21) {
window.setStatusBarColor(TRANSPARENT);
} else if (SDK_INT >= 19) {
// we can't make the status bar transparent, but translucent
window.addFlags(FLAG_TRANSLUCENT_STATUS);
}
// Toolbar
appBarLayout = findViewById(R.id.appBarLayout);
Toolbar toolbar = requireNonNull(setUpCustomToolbar(true));
TextView contactName = toolbar.findViewById(R.id.contactName);
TextView dateView = toolbar.findViewById(R.id.dateView);
// Intent Extras
AttachmentItem attachment = getIntent().getParcelableExtra(ATTACHMENT);
String name = getIntent().getStringExtra(NAME);
long time = getIntent().getLongExtra(DATE, 0);
String date = formatDateAbsolute(this, time);
contactName.setText(name);
dateView.setText(date);
// Image View
photoView = findViewById(R.id.photoView);
if (SDK_INT >= 16) {
photoView.setOnClickListener(view -> toggleSystemUi());
window.getDecorView().setSystemUiVisibility(
SYSTEM_UI_FLAG_LAYOUT_STABLE |
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
// Request Listener
RequestListener<Drawable> listener = new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e,
Object model, Target<Drawable> target,
boolean isFirstResource) {
supportStartPostponedEnterTransition();
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model,
Target<Drawable> target, DataSource dataSource,
boolean isFirstResource) {
if (SDK_INT >= 21 && !(resource instanceof Animatable)) {
// set transition name only when not animatable,
// because the animation won't start otherwise
photoView.setTransitionName(
attachment.getTransitionName());
}
// Move image to the top if overlapping toolbar
if (isOverlappingToolbar(resource)) {
photoView.setScaleType(FIT_START);
}
supportStartPostponedEnterTransition();
return false;
}
};
// Load Image
GlideApp.with(this)
.load(attachment)
.diskCacheStrategy(NONE)
.error(R.drawable.ic_image_broken)
.dontTransform()
.addListener(listener)
.into(photoView);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onPullStart() {
appBarLayout.animate()
.alpha(0f)
.start();
}
@Override
public void onPull(float progress) {
layout.getBackground().setAlpha(Math.round((1 - progress) * 255));
}
@Override
public void onPullCancel() {
appBarLayout.animate()
.alpha(1f)
.start();
}
@Override
public void onPullComplete() {
supportFinishAfterTransition();
}
@RequiresApi(api = 16)
private void toggleSystemUi() {
View decorView = getWindow().getDecorView();
if (appBarLayout.getVisibility() == VISIBLE) {
hideSystemUi(decorView);
} else {
showSystemUi(decorView);
}
}
@RequiresApi(api = 16)
private void hideSystemUi(View decorView) {
decorView.setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE
| SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| SYSTEM_UI_FLAG_FULLSCREEN
);
appBarLayout.animate()
.translationYBy(-1 * appBarLayout.getHeight())
.alpha(0f)
.withEndAction(() -> appBarLayout.setVisibility(GONE))
.start();
}
@RequiresApi(api = 16)
private void showSystemUi(View decorView) {
decorView.setSystemUiVisibility(
SYSTEM_UI_FLAG_LAYOUT_STABLE
| SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
);
appBarLayout.animate()
.translationYBy(appBarLayout.getHeight())
.alpha(1f)
.withStartAction(() -> appBarLayout.setVisibility(VISIBLE))
.start();
}
private boolean isOverlappingToolbar(Drawable drawable) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
float widthPercentage = photoView.getWidth() / (float) width;
float heightPercentage = photoView.getHeight() / (float) height;
float scaleFactor = Math.min(widthPercentage, heightPercentage);
int realWidth = (int) (width * scaleFactor);
int realHeight = (int) (height * scaleFactor);
// return if photo doesn't use the full width,
// because it will be moved to the right otherwise
if (realWidth < photoView.getWidth()) return false;
int drawableTop = (photoView.getHeight() - realHeight) / 2;
return drawableTop < appBarLayout.getBottom() &&
drawableTop != appBarLayout.getTop();
}
}

View File

@@ -0,0 +1,85 @@
package org.briarproject.briar.android.conversation.glide;
import android.support.annotation.Nullable;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.data.DataFetcher;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.conversation.AttachmentItem;
import org.briarproject.briar.api.messaging.MessagingManager;
import java.io.InputStream;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static com.bumptech.glide.load.DataSource.LOCAL;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
@NotNullByDefault
class BriarDataFetcher implements DataFetcher<InputStream> {
private final static Logger LOG =
getLogger(BriarDataFetcher.class.getName());
private final MessagingManager messagingManager;
@DatabaseExecutor
private final Executor dbExecutor;
private final AttachmentItem attachment;
@Nullable
private volatile InputStream inputStream;
private volatile boolean cancel = false;
@Inject
public BriarDataFetcher(MessagingManager messagingManager,
@DatabaseExecutor Executor dbExecutor, AttachmentItem attachment) {
this.messagingManager = messagingManager;
this.dbExecutor = dbExecutor;
this.attachment = attachment;
}
@Override
public void loadData(Priority priority,
DataCallback<? super InputStream> callback) {
MessageId id = attachment.getMessageId();
dbExecutor.execute(() -> {
if (cancel) return;
try {
inputStream = messagingManager.getAttachment(id).getStream();
callback.onDataReady(inputStream);
} catch (DbException e) {
callback.onLoadFailed(e);
}
});
}
@Override
public void cleanup() {
tryToClose(inputStream, LOG, WARNING);
}
@Override
public void cancel() {
cancel = true;
}
@Override
public Class<InputStream> getDataClass() {
return InputStream.class;
}
@Override
public DataSource getDataSource() {
return LOCAL;
}
}

View File

@@ -0,0 +1,30 @@
package org.briarproject.briar.android.conversation.glide;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.conversation.AttachmentItem;
import org.briarproject.briar.api.messaging.MessagingManager;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@NotNullByDefault
public class BriarDataFetcherFactory {
private final MessagingManager messagingManager;
@DatabaseExecutor
private final Executor dbExecutor;
@Inject
public BriarDataFetcherFactory(MessagingManager messagingManager,
@DatabaseExecutor Executor dbExecutor) {
this.messagingManager = messagingManager;
this.dbExecutor = dbExecutor;
}
BriarDataFetcher createBriarDataFetcher(AttachmentItem model) {
return new BriarDataFetcher(messagingManager, dbExecutor, model);
}
}

Some files were not shown because too many files have changed in this diff Show More