mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
45 Commits
release-1.
...
block-inpu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
707066f1a1 | ||
|
|
65e5dc266f | ||
|
|
3487a7cfee | ||
|
|
eea453fc5f | ||
|
|
c6f460a936 | ||
|
|
a5c9e7c74d | ||
|
|
8a4a343147 | ||
|
|
7b22d3b84d | ||
|
|
c8fa23273f | ||
|
|
fbe5df8938 | ||
|
|
008cf95741 | ||
|
|
3eb066a836 | ||
|
|
674b29af25 | ||
|
|
b8ca5ab557 | ||
|
|
6e17709f46 | ||
|
|
726d90145c | ||
|
|
165211eb9b | ||
|
|
868c61e5d6 | ||
|
|
798bb6d4f7 | ||
|
|
bc352a2dc6 | ||
|
|
ce7d6d3db5 | ||
|
|
61276c81d2 | ||
|
|
c09abdb088 | ||
|
|
45a11badd5 | ||
|
|
152ac3df43 | ||
|
|
dd5ad86db8 | ||
|
|
10e9fb308d | ||
|
|
de8e95692a | ||
|
|
d6b52cf4ec | ||
|
|
8a839fb5e4 | ||
|
|
fbf8642edb | ||
|
|
ade6a14342 | ||
|
|
d500ff81c3 | ||
|
|
3053e3cfa7 | ||
|
|
6964a67ca3 | ||
|
|
f4b06e1fb3 | ||
|
|
4db075d643 | ||
|
|
78a8ae6b8e | ||
|
|
7866037d02 | ||
|
|
35716051fb | ||
|
|
6cafea836f | ||
|
|
bd0fd229c6 | ||
|
|
ea05a5c703 | ||
|
|
4103eaf639 | ||
|
|
753a25bc2a |
@@ -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);
|
||||||
|
}
|
||||||
@@ -15,4 +15,8 @@ import dagger.Module;
|
|||||||
})
|
})
|
||||||
public class BrambleAndroidModule {
|
public class BrambleAndroidModule {
|
||||||
|
|
||||||
|
public static void initEagerSingletons(BrambleAndroidEagerSingletons c) {
|
||||||
|
c.inject(new AndroidBatteryModule.EagerSingletons());
|
||||||
|
c.inject(new AndroidNetworkModule.EagerSingletons());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.battery;
|
|||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@@ -11,6 +12,11 @@ import dagger.Provides;
|
|||||||
@Module
|
@Module
|
||||||
public class AndroidBatteryModule {
|
public class AndroidBatteryModule {
|
||||||
|
|
||||||
|
public static class EagerSingletons {
|
||||||
|
@Inject
|
||||||
|
BatteryManager batteryManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
BatteryManager provideBatteryManager(LifecycleManager lifecycleManager,
|
BatteryManager provideBatteryManager(LifecycleManager lifecycleManager,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.network;
|
|||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@@ -11,6 +12,11 @@ import dagger.Provides;
|
|||||||
@Module
|
@Module
|
||||||
public class AndroidNetworkModule {
|
public class AndroidNetworkModule {
|
||||||
|
|
||||||
|
public static class EagerSingletons {
|
||||||
|
@Inject
|
||||||
|
NetworkManager networkManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
NetworkManager provideNetworkManager(LifecycleManager lifecycleManager,
|
NetworkManager provideNetworkManager(LifecycleManager lifecycleManager,
|
||||||
|
|||||||
@@ -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.AndroidExecutor;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
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.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -161,11 +160,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void tryToClose(@Nullable BluetoothServerSocket ss) {
|
void tryToClose(@Nullable BluetoothServerSocket ss) {
|
||||||
try {
|
IoUtils.tryToClose(ss, LOG, WARNING);
|
||||||
if (ss != null) ss.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -195,7 +190,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
s.connect();
|
s.connect();
|
||||||
return wrapSocket(s);
|
return wrapSocket(s);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
tryToClose(s);
|
IoUtils.tryToClose(s, LOG, WARNING);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,14 +263,6 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
return addresses;
|
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 {
|
private class BluetoothStateReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.system;
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
@@ -26,7 +27,7 @@ import static android.provider.Settings.Secure.ANDROID_ID;
|
|||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
|
class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
||||||
|
|
||||||
private static final int SEED_LENGTH = 32;
|
private static final int SEED_LENGTH = 32;
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
|
|||||||
appContext = app.getApplicationContext();
|
appContext = app.getApplicationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("HardwareIds")
|
||||||
@Override
|
@Override
|
||||||
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
|
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
|
||||||
super.writeToEntropyPool(out);
|
super.writeToEntropyPool(out);
|
||||||
@@ -49,12 +51,14 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
|
|||||||
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
||||||
if (id != null) out.writeUTF(id);
|
if (id != null) out.writeUTF(id);
|
||||||
Parcel parcel = Parcel.obtain();
|
Parcel parcel = Parcel.obtain();
|
||||||
WifiManager wm =
|
WifiManager wm = (WifiManager) appContext.getApplicationContext()
|
||||||
(WifiManager) appContext.getSystemService(WIFI_SERVICE);
|
.getSystemService(WIFI_SERVICE);
|
||||||
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
|
if (wm != null) {
|
||||||
if (configs != null) {
|
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
|
||||||
for (WifiConfiguration config : configs)
|
if (configs != null) {
|
||||||
parcel.writeParcelable(config, 0);
|
for (WifiConfiguration config : configs)
|
||||||
|
parcel.writeParcelable(config, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
if (bt != null) {
|
if (bt != null) {
|
||||||
@@ -77,13 +81,13 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
|
|||||||
|
|
||||||
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
private void applyOpenSslFix() {
|
private void applyOpenSslFix() {
|
||||||
byte[] seed = new LinuxSecureRandomSpi().engineGenerateSeed(
|
byte[] seed = new UnixSecureRandomSpi().engineGenerateSeed(
|
||||||
SEED_LENGTH);
|
SEED_LENGTH);
|
||||||
try {
|
try {
|
||||||
// Seed the OpenSSL PRNG
|
// Seed the OpenSSL PRNG
|
||||||
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
.getMethod("RAND_seed", byte[].class)
|
.getMethod("RAND_seed", byte[].class)
|
||||||
.invoke(null, seed);
|
.invoke(null, (Object) seed);
|
||||||
// Mix the output of the Linux PRNG into the OpenSSL PRNG
|
// Mix the output of the Linux PRNG into the OpenSSL PRNG
|
||||||
int bytesRead = (Integer) Class.forName(
|
int bytesRead = (Integer) Class.forName(
|
||||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
|
|||||||
@@ -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.FormatException;
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
|
||||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.data.MetadataParser;
|
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.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
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;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
@@ -62,5 +61,4 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook {
|
|||||||
throw new InvalidMessageException(e);
|
throw new InvalidMessageException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ import org.briarproject.bramble.api.sync.Group;
|
|||||||
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageContext;
|
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 org.briarproject.bramble.api.system.Clock;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.briarproject.bramble.api.sync.MessageId;
|
|||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||||
import org.briarproject.bramble.api.sync.Offer;
|
import org.briarproject.bramble.api.sync.Offer;
|
||||||
import org.briarproject.bramble.api.sync.Request;
|
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.KeySet;
|
||||||
import org.briarproject.bramble.api.transport.KeySetId;
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
@@ -28,8 +29,6 @@ import java.util.Map;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates the database implementation and exposes high-level operations
|
* Encapsulates the database implementation and exposes high-level operations
|
||||||
* to other components.
|
* to other components.
|
||||||
@@ -209,6 +208,24 @@ public interface DatabaseComponent {
|
|||||||
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
|
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
|
||||||
int maxLength, int maxLatency) throws DbException;
|
int maxLength, int maxLatency) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of blocks in the given message.
|
||||||
|
* <p>
|
||||||
|
* Read-only.
|
||||||
|
*/
|
||||||
|
int getBlockCount(Transaction txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the given block of the given message.
|
||||||
|
* <p>
|
||||||
|
* Read-only.
|
||||||
|
*
|
||||||
|
* @throws NoSuchBlockException if 'blockNumber' is greater than or equal
|
||||||
|
* to the number of blocks in the message
|
||||||
|
*/
|
||||||
|
byte[] getBlock(Transaction txn, MessageId m, int blockNumber)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the contact with the given ID.
|
* Returns the contact with the given ID.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -374,12 +391,12 @@ public interface DatabaseComponent {
|
|||||||
/**
|
/**
|
||||||
* Returns the IDs and states of all dependencies of the given message.
|
* Returns the IDs and states of all dependencies of the given message.
|
||||||
* For missing dependencies and dependencies in other groups, the state
|
* For missing dependencies and dependencies in other groups, the state
|
||||||
* {@link State UNKNOWN} is returned.
|
* {@link MessageState UNKNOWN} is returned.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Map<MessageId, State> getMessageDependencies(Transaction txn, MessageId m)
|
Map<MessageId, MessageState> getMessageDependencies(Transaction txn,
|
||||||
throws DbException;
|
MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs and states of all dependents of the given message.
|
* Returns the IDs and states of all dependents of the given message.
|
||||||
@@ -388,15 +405,16 @@ public interface DatabaseComponent {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Map<MessageId, State> getMessageDependents(Transaction txn, MessageId m)
|
Map<MessageId, MessageState> getMessageDependents(Transaction txn,
|
||||||
throws DbException;
|
MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the validation and delivery state of the given message.
|
* Gets the validation and delivery state of the given message.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* 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
|
* Returns the status of the given delivered message with respect to the
|
||||||
@@ -543,7 +561,7 @@ public interface DatabaseComponent {
|
|||||||
/**
|
/**
|
||||||
* Sets the validation and delivery state of the given message.
|
* 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;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.briarproject.bramble.api.db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a database operation is attempted for a block that is not in
|
||||||
|
* the database. This exception may occur due to concurrent updates and does
|
||||||
|
* not indicate a database error.
|
||||||
|
*/
|
||||||
|
public class NoSuchBlockException extends DbException {
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package org.briarproject.bramble.api.io;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.db.NoSuchBlockException;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
public interface BlockSource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of blocks in the given message.
|
||||||
|
*/
|
||||||
|
int getBlockCount(MessageId m) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the given block of the given message.
|
||||||
|
*
|
||||||
|
* @throws NoSuchBlockException if 'blockNumber' is greater than or equal
|
||||||
|
* to the number of blocks in the message
|
||||||
|
*/
|
||||||
|
byte[] getBlock(MessageId m, int blockNumber) throws DbException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.briarproject.bramble.api.io;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public interface MessageInputStreamFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@link InputStream} for reading the given message from the
|
||||||
|
* database. This method returns immediately. If the message is not in the
|
||||||
|
* database or cannot be read, reading from the stream will throw an
|
||||||
|
* {@link IOException};
|
||||||
|
*/
|
||||||
|
InputStream getMessageInputStream(MessageId m);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,4 +35,9 @@ public interface SyncConstants {
|
|||||||
* The maximum number of message IDs in an ack, offer or request record.
|
* The maximum number of message IDs in an ack, offer or request record.
|
||||||
*/
|
*/
|
||||||
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH;
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,11 +3,10 @@ package org.briarproject.bramble.api.sync.event;
|
|||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event that is broadcast when a message state changed.
|
* 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 MessageId messageId;
|
||||||
private final boolean local;
|
private final boolean local;
|
||||||
private final State state;
|
private final MessageState state;
|
||||||
|
|
||||||
public MessageStateChangedEvent(MessageId messageId, boolean local,
|
public MessageStateChangedEvent(MessageId messageId, boolean local,
|
||||||
State state) {
|
MessageState state) {
|
||||||
this.messageId = messageId;
|
this.messageId = messageId;
|
||||||
this.local = local;
|
this.local = local;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
@@ -34,7 +33,7 @@ public class MessageStateChangedEvent extends Event {
|
|||||||
return local;
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
public State getState() {
|
public MessageState getState() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -8,12 +8,15 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class IoUtils {
|
public class IoUtils {
|
||||||
@@ -54,16 +57,35 @@ public class IoUtils {
|
|||||||
out.flush();
|
out.flush();
|
||||||
out.close();
|
out.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
tryToClose(in);
|
tryToClose(in, LOG, WARNING);
|
||||||
tryToClose(out);
|
tryToClose(out, LOG, WARNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void tryToClose(@Nullable Closeable c) {
|
public static void tryToClose(@Nullable Closeable c, Logger logger,
|
||||||
|
Level level) {
|
||||||
try {
|
try {
|
||||||
if (c != null) c.close();
|
if (c != null) c.close();
|
||||||
} catch (IOException e) {
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ public class OsUtils {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private static final String os = System.getProperty("os.name");
|
private static final String os = System.getProperty("os.name");
|
||||||
@Nullable
|
@Nullable
|
||||||
private static final String version = System.getProperty("os.version");
|
|
||||||
@Nullable
|
|
||||||
private static final String vendor = System.getProperty("java.vendor");
|
private static final String vendor = System.getProperty("java.vendor");
|
||||||
|
|
||||||
public static boolean isWindows() {
|
public static boolean isWindows() {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.briarproject.bramble.lifecycle.LifecycleModule;
|
|||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
import org.briarproject.bramble.properties.PropertiesModule;
|
import org.briarproject.bramble.properties.PropertiesModule;
|
||||||
import org.briarproject.bramble.reporting.ReportingModule;
|
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.system.SystemModule;
|
||||||
import org.briarproject.bramble.transport.TransportModule;
|
import org.briarproject.bramble.transport.TransportModule;
|
||||||
import org.briarproject.bramble.versioning.VersioningModule;
|
import org.briarproject.bramble.versioning.VersioningModule;
|
||||||
@@ -31,11 +31,11 @@ public interface BrambleCoreEagerSingletons {
|
|||||||
|
|
||||||
void inject(ReportingModule.EagerSingletons init);
|
void inject(ReportingModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(SyncModule.EagerSingletons init);
|
|
||||||
|
|
||||||
void inject(SystemModule.EagerSingletons init);
|
void inject(SystemModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(TransportModule.EagerSingletons init);
|
void inject(TransportModule.EagerSingletons init);
|
||||||
|
|
||||||
|
void inject(ValidationModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(VersioningModule.EagerSingletons init);
|
void inject(VersioningModule.EagerSingletons init);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
|
import org.briarproject.bramble.io.IoModule;
|
||||||
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
@@ -19,6 +20,7 @@ import org.briarproject.bramble.reporting.ReportingModule;
|
|||||||
import org.briarproject.bramble.settings.SettingsModule;
|
import org.briarproject.bramble.settings.SettingsModule;
|
||||||
import org.briarproject.bramble.socks.SocksModule;
|
import org.briarproject.bramble.socks.SocksModule;
|
||||||
import org.briarproject.bramble.sync.SyncModule;
|
import org.briarproject.bramble.sync.SyncModule;
|
||||||
|
import org.briarproject.bramble.sync.validation.ValidationModule;
|
||||||
import org.briarproject.bramble.system.SystemModule;
|
import org.briarproject.bramble.system.SystemModule;
|
||||||
import org.briarproject.bramble.transport.TransportModule;
|
import org.briarproject.bramble.transport.TransportModule;
|
||||||
import org.briarproject.bramble.versioning.VersioningModule;
|
import org.briarproject.bramble.versioning.VersioningModule;
|
||||||
@@ -35,6 +37,7 @@ import dagger.Module;
|
|||||||
DatabaseExecutorModule.class,
|
DatabaseExecutorModule.class,
|
||||||
EventModule.class,
|
EventModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
|
IoModule.class,
|
||||||
KeyAgreementModule.class,
|
KeyAgreementModule.class,
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
@@ -47,6 +50,7 @@ import dagger.Module;
|
|||||||
SyncModule.class,
|
SyncModule.class,
|
||||||
SystemModule.class,
|
SystemModule.class,
|
||||||
TransportModule.class,
|
TransportModule.class,
|
||||||
|
ValidationModule.class,
|
||||||
VersioningModule.class
|
VersioningModule.class
|
||||||
})
|
})
|
||||||
public class BrambleCoreModule {
|
public class BrambleCoreModule {
|
||||||
@@ -60,9 +64,9 @@ public class BrambleCoreModule {
|
|||||||
c.inject(new PluginModule.EagerSingletons());
|
c.inject(new PluginModule.EagerSingletons());
|
||||||
c.inject(new PropertiesModule.EagerSingletons());
|
c.inject(new PropertiesModule.EagerSingletons());
|
||||||
c.inject(new ReportingModule.EagerSingletons());
|
c.inject(new ReportingModule.EagerSingletons());
|
||||||
c.inject(new SyncModule.EagerSingletons());
|
|
||||||
c.inject(new SystemModule.EagerSingletons());
|
c.inject(new SystemModule.EagerSingletons());
|
||||||
c.inject(new TransportModule.EagerSingletons());
|
c.inject(new TransportModule.EagerSingletons());
|
||||||
|
c.inject(new ValidationModule.EagerSingletons());
|
||||||
c.inject(new VersioningModule.EagerSingletons());
|
c.inject(new VersioningModule.EagerSingletons());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.briarproject.bramble.db;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.io.BlockSource;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
class BlockSourceImpl implements BlockSource {
|
||||||
|
|
||||||
|
private final DatabaseComponent db;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
BlockSourceImpl(DatabaseComponent db) {
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBlockCount(MessageId m) throws DbException {
|
||||||
|
return db.transactionWithResult(true, txn -> db.getBlockCount(txn, m));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBlock(MessageId m, int blockNumber) throws DbException {
|
||||||
|
return db.transactionWithResult(true, txn ->
|
||||||
|
db.getBlock(txn, m, blockNumber));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ import org.briarproject.bramble.api.sync.GroupId;
|
|||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
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.KeySet;
|
||||||
import org.briarproject.bramble.api.transport.KeySetId;
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
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
|
* @param sender the contact from whom the message was received, or null
|
||||||
* if the message was created locally.
|
* 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;
|
@Nullable ContactId sender) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,7 +114,7 @@ interface Database<T> {
|
|||||||
* in the given state.
|
* in the given state.
|
||||||
*/
|
*/
|
||||||
void addMessageDependency(T txn, Message dependent, MessageId dependency,
|
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.
|
* 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.
|
* Returns the IDs and states of all dependencies of the given message.
|
||||||
* For missing dependencies and dependencies in other groups, the state
|
* For missing dependencies and dependencies in other groups, the state
|
||||||
* {@link State UNKNOWN} is returned.
|
* {@link MessageState UNKNOWN} is returned.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Map<MessageId, State> getMessageDependencies(T txn, MessageId m)
|
Map<MessageId, MessageState> getMessageDependencies(T txn, MessageId m)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -324,7 +324,7 @@ interface Database<T> {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Map<MessageId, State> getMessageDependents(T txn, MessageId m)
|
Map<MessageId, MessageState> getMessageDependents(T txn, MessageId m)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -383,7 +383,7 @@ interface Database<T> {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* 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
|
* 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.
|
* 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
|
* Sets the reordering window for the given key set and transport in the
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.DbRunnable;
|
import org.briarproject.bramble.api.db.DbRunnable;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
import org.briarproject.bramble.api.db.Metadata;
|
||||||
import org.briarproject.bramble.api.db.MigrationListener;
|
import org.briarproject.bramble.api.db.MigrationListener;
|
||||||
|
import org.briarproject.bramble.api.db.NoSuchBlockException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchGroupException;
|
import org.briarproject.bramble.api.db.NoSuchGroupException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
|
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
|
||||||
@@ -43,7 +44,6 @@ import org.briarproject.bramble.api.sync.MessageId;
|
|||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||||
import org.briarproject.bramble.api.sync.Offer;
|
import org.briarproject.bramble.api.sync.Offer;
|
||||||
import org.briarproject.bramble.api.sync.Request;
|
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.GroupAddedEvent;
|
||||||
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
|
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
|
||||||
import org.briarproject.bramble.api.sync.event.GroupVisibilityUpdatedEvent;
|
import org.briarproject.bramble.api.sync.event.GroupVisibilityUpdatedEvent;
|
||||||
@@ -55,6 +55,7 @@ import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
|
|||||||
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
|
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
|
||||||
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
||||||
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
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.KeySet;
|
||||||
import org.briarproject.bramble.api.transport.KeySetId;
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
@@ -75,8 +76,8 @@ import javax.inject.Inject;
|
|||||||
import static java.util.logging.Level.WARNING;
|
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.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
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.validation.MessageState.DELIVERED;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
|
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.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
@@ -420,6 +421,26 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBlockCount(Transaction transaction, MessageId m)
|
||||||
|
throws DbException {
|
||||||
|
T txn = unbox(transaction);
|
||||||
|
if (!db.containsMessage(txn, m))
|
||||||
|
throw new NoSuchMessageException();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBlock(Transaction transaction, MessageId m,
|
||||||
|
int blockNumber) throws DbException {
|
||||||
|
T txn = unbox(transaction);
|
||||||
|
if (!db.containsMessage(txn, m))
|
||||||
|
throw new NoSuchMessageException();
|
||||||
|
if (blockNumber != 0)
|
||||||
|
throw new NoSuchBlockException();
|
||||||
|
return db.getMessage(txn, m).getBody();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Contact getContact(Transaction transaction, ContactId c)
|
public Contact getContact(Transaction transaction, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -579,7 +600,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public State getMessageState(Transaction transaction, MessageId m)
|
public MessageState getMessageState(Transaction transaction, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsMessage(txn, m))
|
if (!db.containsMessage(txn, m))
|
||||||
@@ -619,8 +640,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<MessageId, State> getMessageDependencies(Transaction transaction,
|
public Map<MessageId, MessageState> getMessageDependencies(
|
||||||
MessageId m) throws DbException {
|
Transaction transaction, MessageId m) throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsMessage(txn, m))
|
if (!db.containsMessage(txn, m))
|
||||||
throw new NoSuchMessageException();
|
throw new NoSuchMessageException();
|
||||||
@@ -628,8 +649,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<MessageId, State> getMessageDependents(Transaction transaction,
|
public Map<MessageId, MessageState> getMessageDependents(
|
||||||
MessageId m) throws DbException {
|
Transaction transaction, MessageId m) throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsMessage(txn, m))
|
if (!db.containsMessage(txn, m))
|
||||||
throw new NoSuchMessageException();
|
throw new NoSuchMessageException();
|
||||||
@@ -918,7 +939,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMessageState(Transaction transaction, MessageId m,
|
public void setMessageState(Transaction transaction, MessageId m,
|
||||||
State state) throws DbException {
|
MessageState state) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsMessage(txn, m))
|
if (!db.containsMessage(txn, m))
|
||||||
@@ -935,10 +956,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsMessage(txn, dependent.getId()))
|
if (!db.containsMessage(txn, dependent.getId()))
|
||||||
throw new NoSuchMessageException();
|
throw new NoSuchMessageException();
|
||||||
State dependentState = db.getMessageState(txn, dependent.getId());
|
MessageState dependentState =
|
||||||
|
db.getMessageState(txn, dependent.getId());
|
||||||
for (MessageId dependency : dependencies) {
|
for (MessageId dependency : dependencies) {
|
||||||
db.addMessageDependency(txn, dependent, dependency,
|
db.addMessageDependency(txn, dependent, dependency, dependentState);
|
||||||
dependentState);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.db;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.io.BlockSource;
|
||||||
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
@@ -31,4 +32,9 @@ public class DatabaseModule {
|
|||||||
return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
|
return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
|
||||||
shutdown);
|
shutdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
BlockSource provideBlockSource(BlockSourceImpl blockSource) {
|
||||||
|
return blockSource;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,16 +15,23 @@ import java.sql.DriverManager;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
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.
|
* Contains all the H2-specific code for the database.
|
||||||
*/
|
*/
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class H2Database extends JdbcDatabase {
|
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 HASH_TYPE = "BINARY(32)";
|
||||||
private static final String SECRET_TYPE = "BINARY(32)";
|
private static final String SECRET_TYPE = "BINARY(32)";
|
||||||
private static final String BINARY_TYPE = "BINARY";
|
private static final String BINARY_TYPE = "BINARY";
|
||||||
@@ -121,8 +128,8 @@ class H2Database extends JdbcDatabase {
|
|||||||
s.close();
|
s.close();
|
||||||
c.close();
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s);
|
tryToClose(s, LOG, WARNING);
|
||||||
tryToClose(c);
|
tryToClose(c, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,24 @@ import java.sql.Connection;
|
|||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
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.
|
* Contains all the HSQLDB-specific code for the database.
|
||||||
*/
|
*/
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class HyperSqlDatabase extends JdbcDatabase {
|
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 HASH_TYPE = "BINARY(32)";
|
||||||
private static final String SECRET_TYPE = "BINARY(32)";
|
private static final String SECRET_TYPE = "BINARY(32)";
|
||||||
private static final String BINARY_TYPE = "BINARY";
|
private static final String BINARY_TYPE = "BINARY";
|
||||||
@@ -72,8 +80,8 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
s.close();
|
s.close();
|
||||||
c.close();
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s);
|
tryToClose(s, LOG, WARNING);
|
||||||
tryToClose(c);
|
tryToClose(c, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,8 +130,8 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
s.close();
|
s.close();
|
||||||
c.close();
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s);
|
tryToClose(s, LOG, WARNING);
|
||||||
tryToClose(c);
|
tryToClose(c, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import org.briarproject.bramble.api.sync.Message;
|
|||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
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.system.Clock;
|
||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
import org.briarproject.bramble.api.transport.KeySet;
|
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.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
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.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||||
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.api.sync.ValidationManager.State.PENDING;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
|
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.DB_SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
|
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.MAX_COMPACTION_INTERVAL_MS;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
||||||
import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
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.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
@@ -458,30 +459,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
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 {
|
private void createTables(Connection txn) throws DbException {
|
||||||
Statement s = null;
|
Statement s = null;
|
||||||
try {
|
try {
|
||||||
@@ -502,7 +479,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
|
||||||
s.close();
|
s.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s);
|
tryToClose(s, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -519,7 +496,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
|
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
|
||||||
s.close();
|
s.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s);
|
tryToClose(s, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -566,11 +543,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
// Try to close the connection
|
// Try to close the connection
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
try {
|
tryToClose(txn, LOG, WARNING);
|
||||||
txn.close();
|
|
||||||
} catch (SQLException e1) {
|
|
||||||
logException(LOG, WARNING, e1);
|
|
||||||
}
|
|
||||||
// Whatever happens, allow the database to close
|
// Whatever happens, allow the database to close
|
||||||
connectionsLock.lock();
|
connectionsLock.lock();
|
||||||
try {
|
try {
|
||||||
@@ -659,8 +632,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return c;
|
return c;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -681,7 +654,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -704,7 +677,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
// Create a status row for each message in the group
|
// Create a status row for each message in the group
|
||||||
addStatus(txn, c, g, groupShared);
|
addStatus(txn, c, g, groupShared);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -724,7 +697,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
MessageId id = new MessageId(rs.getBytes(1));
|
MessageId id = new MessageId(rs.getBytes(1));
|
||||||
long timestamp = rs.getLong(2);
|
long timestamp = rs.getLong(2);
|
||||||
State state = State.fromValue(rs.getInt(3));
|
MessageState state = MessageState.fromValue(rs.getInt(3));
|
||||||
boolean messageShared = rs.getBoolean(4);
|
boolean messageShared = rs.getBoolean(4);
|
||||||
int length = rs.getInt(5);
|
int length = rs.getInt(5);
|
||||||
boolean deleted = rs.getBoolean(6);
|
boolean deleted = rs.getBoolean(6);
|
||||||
@@ -735,8 +708,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -761,13 +734,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addMessage(Connection txn, Message m, State state,
|
public void addMessage(Connection txn, Message m, MessageState state,
|
||||||
boolean messageShared, @Nullable ContactId sender)
|
boolean messageShared, @Nullable ContactId sender)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -810,7 +783,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0) throw new DbStateException();
|
if (affected < 0) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -840,14 +813,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addStatus(Connection txn, MessageId m, ContactId c, GroupId g,
|
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)
|
boolean messageShared, boolean deleted, boolean seen)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -873,14 +846,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addMessageDependency(Connection txn, Message dependent,
|
public void addMessageDependency(Connection txn, Message dependent,
|
||||||
MessageId dependency, State dependentState) throws DbException {
|
MessageId dependency, MessageState dependentState)
|
||||||
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
@@ -891,9 +865,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setBytes(1, dependency.getBytes());
|
ps.setBytes(1, dependency.getBytes());
|
||||||
ps.setBytes(2, dependent.getGroupId().getBytes());
|
ps.setBytes(2, dependent.getGroupId().getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
State dependencyState = null;
|
MessageState dependencyState = null;
|
||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
dependencyState = State.fromValue(rs.getInt(1));
|
dependencyState = MessageState.fromValue(rs.getInt(1));
|
||||||
if (rs.next()) throw new DbStateException();
|
if (rs.next()) throw new DbStateException();
|
||||||
}
|
}
|
||||||
rs.close();
|
rs.close();
|
||||||
@@ -914,8 +888,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -934,7 +908,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1014,8 +988,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return keySetId;
|
return keySetId;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1038,8 +1012,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1060,8 +1034,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1082,8 +1056,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1104,8 +1078,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1126,8 +1100,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1148,8 +1122,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1173,8 +1147,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1197,8 +1171,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return count;
|
return count;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1222,7 +1196,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0) throw new DbStateException();
|
if (affected < 0) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1239,7 +1213,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0) throw new DbStateException();
|
if (affected < 0) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1272,8 +1246,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
return new Contact(c, author, localAuthorId, alias, verified,
|
return new Contact(c, author, localAuthorId, alias, verified,
|
||||||
active);
|
active);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1309,8 +1283,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return contacts;
|
return contacts;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1332,8 +1306,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1370,8 +1344,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return contacts;
|
return contacts;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1394,8 +1368,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return new Group(g, clientId, majorVersion, descriptor);
|
return new Group(g, clientId, majorVersion, descriptor);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1422,8 +1396,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return groups;
|
return groups;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1448,8 +1422,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return v;
|
return v;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1472,8 +1446,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return visible;
|
return visible;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1504,8 +1478,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return localAuthor;
|
return localAuthor;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1536,8 +1510,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return authors;
|
return authors;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1565,8 +1539,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
|
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
|
||||||
return new Message(m, g, timestamp, body);
|
return new Message(m, g, timestamp, body);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1589,8 +1563,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1627,8 +1601,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (intersection == null) throw new AssertionError();
|
if (intersection == null) throw new AssertionError();
|
||||||
return intersection;
|
return intersection;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1660,8 +1634,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return all;
|
return all;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1695,8 +1669,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return metadata;
|
return metadata;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1719,8 +1693,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return metadata;
|
return metadata;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1745,8 +1719,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return metadata;
|
return metadata;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1775,8 +1749,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return statuses;
|
return statuses;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1806,14 +1780,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return status;
|
return status;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<MessageId, State> getMessageDependencies(Connection txn,
|
public Map<MessageId, MessageState> getMessageDependencies(Connection txn,
|
||||||
MessageId m) throws DbException {
|
MessageId m) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
@@ -1824,10 +1798,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, m.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
Map<MessageId, State> dependencies = new HashMap<>();
|
Map<MessageId, MessageState> dependencies = new HashMap<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
MessageId dependency = new MessageId(rs.getBytes(1));
|
MessageId dependency = new MessageId(rs.getBytes(1));
|
||||||
State state = State.fromValue(rs.getInt(2));
|
MessageState state = MessageState.fromValue(rs.getInt(2));
|
||||||
if (rs.wasNull())
|
if (rs.wasNull())
|
||||||
state = UNKNOWN; // Missing or in a different group
|
state = UNKNOWN; // Missing or in a different group
|
||||||
dependencies.put(dependency, state);
|
dependencies.put(dependency, state);
|
||||||
@@ -1836,14 +1810,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return dependencies;
|
return dependencies;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<MessageId, State> getMessageDependents(Connection txn,
|
public Map<MessageId, MessageState> getMessageDependents(Connection txn,
|
||||||
MessageId m) throws DbException {
|
MessageId m) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
@@ -1857,24 +1831,24 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, m.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
Map<MessageId, State> dependents = new HashMap<>();
|
Map<MessageId, MessageState> dependents = new HashMap<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
MessageId dependent = new MessageId(rs.getBytes(1));
|
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);
|
dependents.put(dependent, state);
|
||||||
}
|
}
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
return dependents;
|
return dependents;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public State getMessageState(Connection txn, MessageId m)
|
public MessageState getMessageState(Connection txn, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
@@ -1884,14 +1858,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, m.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
if (!rs.next()) throw new DbStateException();
|
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();
|
if (rs.next()) throw new DbStateException();
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
return state;
|
return state;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1915,8 +1889,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1949,8 +1923,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1974,8 +1948,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2013,8 +1987,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2032,7 +2006,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Collection<MessageId> getMessagesInState(Connection txn,
|
private Collection<MessageId> getMessagesInState(Connection txn,
|
||||||
State state) throws DbException {
|
MessageState state) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
@@ -2047,8 +2021,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2075,8 +2049,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2105,8 +2079,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return nextSendTime;
|
return nextSendTime;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2144,8 +2118,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2167,8 +2141,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return s;
|
return s;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2233,8 +2207,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return keys;
|
return keys;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2253,7 +2227,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2280,7 +2254,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2307,7 +2281,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2337,7 +2311,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (rows != 1) throw new DbStateException();
|
if (rows != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2359,7 +2333,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
if (!rs.next()) throw new DbStateException();
|
if (!rs.next()) throw new DbStateException();
|
||||||
GroupId g = new GroupId(rs.getBytes(1));
|
GroupId g = new GroupId(rs.getBytes(1));
|
||||||
State state = State.fromValue(rs.getInt(2));
|
MessageState state = MessageState.fromValue(rs.getInt(2));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
// Insert any keys that don't already exist
|
// Insert any keys that don't already exist
|
||||||
@@ -2382,8 +2356,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (rows != 1) throw new DbStateException();
|
if (rows != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2449,7 +2423,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
return added;
|
return added;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2496,7 +2470,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (rows != 1) throw new DbStateException();
|
if (rows != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2515,7 +2489,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2534,7 +2508,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2553,7 +2527,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2570,7 +2544,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2586,7 +2560,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2614,7 +2588,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0) throw new DbStateException();
|
if (affected < 0) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2631,7 +2605,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2647,7 +2621,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2666,7 +2640,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return affected == 1;
|
return affected == 1;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2691,7 +2665,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (rows != 1) throw new DbStateException();
|
if (rows != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2708,7 +2682,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2729,7 +2703,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0) throw new DbStateException();
|
if (affected < 0) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2748,7 +2722,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2766,7 +2740,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2784,7 +2758,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2803,7 +2777,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2833,7 +2807,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0) throw new DbStateException();
|
if (affected < 0) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2859,13 +2833,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0) throw new DbStateException();
|
if (affected < 0) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMessageState(Connection txn, MessageId m, State state)
|
public void setMessageState(Connection txn, MessageId m, MessageState state)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
@@ -2912,7 +2886,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0) throw new DbStateException();
|
if (affected < 0) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2935,7 +2909,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2954,7 +2928,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2990,8 +2964,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3058,7 +3032,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (rows < 0 || rows > 1) throw new DbStateException();
|
if (rows < 0 || rows > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,10 +7,8 @@ import java.sql.SQLException;
|
|||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
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> {
|
class Migration38_39 implements Migration<Connection> {
|
||||||
|
|
||||||
@@ -40,16 +38,8 @@ class Migration38_39 implements Migration<Connection> {
|
|||||||
+ " ALTER COLUMN contactId"
|
+ " ALTER COLUMN contactId"
|
||||||
+ " SET NOT NULL");
|
+ " SET NOT NULL");
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s);
|
tryToClose(s, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(@Nullable Statement s) {
|
|
||||||
try {
|
|
||||||
if (s != null) s.close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ import java.sql.SQLException;
|
|||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
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> {
|
class Migration39_40 implements Migration<Connection> {
|
||||||
|
|
||||||
@@ -39,16 +37,8 @@ class Migration39_40 implements Migration<Connection> {
|
|||||||
+ " ALTER COLUMN eta"
|
+ " ALTER COLUMN eta"
|
||||||
+ " SET NOT NULL");
|
+ " SET NOT NULL");
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s);
|
tryToClose(s, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(@Nullable Statement s) {
|
|
||||||
try {
|
|
||||||
if (s != null) s.close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ import java.sql.SQLException;
|
|||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
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> {
|
class Migration40_41 implements Migration<Connection> {
|
||||||
|
|
||||||
@@ -41,16 +39,8 @@ class Migration40_41 implements Migration<Connection> {
|
|||||||
s.execute("ALTER TABLE contacts"
|
s.execute("ALTER TABLE contacts"
|
||||||
+ dbTypes.replaceTypes(" ADD alias _STRING"));
|
+ dbTypes.replaceTypes(" ADD alias _STRING"));
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s);
|
tryToClose(s, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(@Nullable Statement s) {
|
|
||||||
try {
|
|
||||||
if (s != null) s.close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package org.briarproject.bramble.io;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
import static java.lang.System.arraycopy;
|
||||||
|
import static java.lang.Thread.currentThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link InputStream} that asynchronously fetches blocks of data on demand.
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
abstract class BlockInputStream extends InputStream {
|
||||||
|
|
||||||
|
private final int minBufferBytes;
|
||||||
|
private final BlockingQueue<Buffer> queue = new ArrayBlockingQueue<>(1);
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
@Nullable
|
||||||
|
private Buffer buffer = null;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private int offset = 0;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private boolean fetchingBlock = false;
|
||||||
|
|
||||||
|
abstract void fetchBlockAsync(int blockNumber);
|
||||||
|
|
||||||
|
BlockInputStream(int minBufferBytes) {
|
||||||
|
this.minBufferBytes = minBufferBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!prepareRead()) return -1;
|
||||||
|
if (buffer == null) throw new AssertionError();
|
||||||
|
return buffer.data[offset++] & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
return read(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
if (off < 0 || len < 0 || off + len > b.length)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!prepareRead()) return -1;
|
||||||
|
if (buffer == null) throw new AssertionError();
|
||||||
|
len = Math.min(len, buffer.length - offset);
|
||||||
|
if (len < 0) throw new AssertionError();
|
||||||
|
arraycopy(buffer.data, offset, b, off, len);
|
||||||
|
offset += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean prepareRead() throws IOException {
|
||||||
|
throwExceptionIfNecessary();
|
||||||
|
if (isEndOfStream()) return false;
|
||||||
|
if (shouldFetchBlock()) fetchBlockAsync();
|
||||||
|
waitForBlock();
|
||||||
|
if (buffer == null) throw new AssertionError();
|
||||||
|
return offset < buffer.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private void throwExceptionIfNecessary() throws IOException {
|
||||||
|
if (buffer != null && buffer.exception != null)
|
||||||
|
throw new IOException(buffer.exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private boolean isEndOfStream() {
|
||||||
|
return buffer != null && offset == buffer.length && !fetchingBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private boolean shouldFetchBlock() {
|
||||||
|
if (fetchingBlock) return false;
|
||||||
|
if (buffer == null) return true;
|
||||||
|
if (buffer.length == 0) return false;
|
||||||
|
return buffer.length - offset < minBufferBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private void fetchBlockAsync() {
|
||||||
|
if (buffer == null) fetchBlockAsync(0);
|
||||||
|
else fetchBlockAsync(buffer.blockNumber + 1);
|
||||||
|
fetchingBlock = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private void waitForBlock() throws IOException {
|
||||||
|
if (buffer != null && offset < buffer.length) return;
|
||||||
|
try {
|
||||||
|
buffer = queue.take();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
currentThread().interrupt();
|
||||||
|
throw new InterruptedIOException();
|
||||||
|
}
|
||||||
|
fetchingBlock = false;
|
||||||
|
offset = 0;
|
||||||
|
throwExceptionIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fetchSucceeded(int blockNumber, byte[] data, int length) {
|
||||||
|
queue.add(new Buffer(blockNumber, data, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
void fetchFailed(int blockNumber, Exception exception) {
|
||||||
|
queue.add(new Buffer(blockNumber, exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Buffer {
|
||||||
|
|
||||||
|
private final int blockNumber;
|
||||||
|
private final byte[] data;
|
||||||
|
private final int length;
|
||||||
|
@Nullable
|
||||||
|
private final Exception exception;
|
||||||
|
|
||||||
|
private Buffer(int blockNumber, byte[] data, int length) {
|
||||||
|
if (length < 0 || length > data.length)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
this.blockNumber = blockNumber;
|
||||||
|
this.data = data;
|
||||||
|
this.length = length;
|
||||||
|
exception = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Buffer(int blockNumber, Exception exception) {
|
||||||
|
this.blockNumber = blockNumber;
|
||||||
|
this.exception = exception;
|
||||||
|
data = new byte[0];
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package org.briarproject.bramble.io;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.io.BlockSource;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link BlockInputStream} that fetches data from a {@link BlockSource}.
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
class BlockSourceInputStream extends BlockInputStream {
|
||||||
|
|
||||||
|
private final Executor executor;
|
||||||
|
private final BlockSource blockSource;
|
||||||
|
private final MessageId messageId;
|
||||||
|
|
||||||
|
private volatile int blockCount = -1;
|
||||||
|
|
||||||
|
BlockSourceInputStream(int minBufferBytes, Executor executor,
|
||||||
|
BlockSource blockSource, MessageId messageId) {
|
||||||
|
super(minBufferBytes);
|
||||||
|
this.executor = executor;
|
||||||
|
this.blockSource = blockSource;
|
||||||
|
this.messageId = messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void fetchBlockAsync(int blockNumber) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
if (blockCount == -1) {
|
||||||
|
blockCount = blockSource.getBlockCount(messageId);
|
||||||
|
}
|
||||||
|
if (blockNumber > blockCount) {
|
||||||
|
fetchFailed(blockNumber, new IllegalArgumentException());
|
||||||
|
} else if (blockNumber == blockCount) {
|
||||||
|
fetchSucceeded(blockNumber, new byte[0], 0); // EOF
|
||||||
|
} else {
|
||||||
|
byte[] block = blockSource.getBlock(messageId, blockNumber);
|
||||||
|
fetchSucceeded(blockNumber, block, block.length);
|
||||||
|
}
|
||||||
|
} catch (DbException e) {
|
||||||
|
fetchFailed(blockNumber, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.briarproject.bramble.io;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.io.MessageInputStreamFactory;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class IoModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
MessageInputStreamFactory provideMessageInputStreamFactory(
|
||||||
|
MessageInputStreamFactoryImpl messageInputStreamFactory) {
|
||||||
|
return messageInputStreamFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.briarproject.bramble.io;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
|
import org.briarproject.bramble.api.io.BlockSource;
|
||||||
|
import org.briarproject.bramble.api.io.MessageInputStreamFactory;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_BLOCK_LENGTH;
|
||||||
|
|
||||||
|
class MessageInputStreamFactoryImpl implements MessageInputStreamFactory {
|
||||||
|
|
||||||
|
private final Executor dbExecutor;
|
||||||
|
private final BlockSource blockSource;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MessageInputStreamFactoryImpl(@DatabaseExecutor Executor dbExecutor,
|
||||||
|
BlockSource blockSource) {
|
||||||
|
this.dbExecutor = dbExecutor;
|
||||||
|
this.blockSource = blockSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getMessageInputStream(MessageId m) {
|
||||||
|
return new BlockSourceInputStream(MAX_BLOCK_LENGTH, dbExecutor,
|
||||||
|
blockSource, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class FileTransportReader implements TransportConnectionReader {
|
class FileTransportReader implements TransportConnectionReader {
|
||||||
@@ -34,11 +33,7 @@ class FileTransportReader implements TransportConnectionReader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose(boolean exception, boolean recognised) {
|
public void dispose(boolean exception, boolean recognised) {
|
||||||
try {
|
tryToClose(in, LOG, WARNING);
|
||||||
in.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
plugin.readerFinished(file, exception, recognised);
|
plugin.readerFinished(file, exception, recognised);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class FileTransportWriter implements TransportConnectionWriter {
|
class FileTransportWriter implements TransportConnectionWriter {
|
||||||
@@ -44,11 +43,7 @@ class FileTransportWriter implements TransportConnectionWriter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose(boolean exception) {
|
public void dispose(boolean exception) {
|
||||||
try {
|
tryToClose(out, LOG, WARNING);
|
||||||
out.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
plugin.writerFinished(file, exception);
|
plugin.writerFinished(file, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.PREF_LAN_IP_PORTS;
|
||||||
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_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.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -293,11 +293,7 @@ class LanTcpPlugin extends TcpPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
try {
|
IoUtils.tryToClose(ss, LOG, WARNING);
|
||||||
ss.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.DuplexPluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -153,13 +154,8 @@ abstract class TcpPlugin implements DuplexPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void tryToClose(@Nullable ServerSocket ss) {
|
protected void tryToClose(@Nullable ServerSocket ss) {
|
||||||
try {
|
IoUtils.tryToClose(ss, LOG, WARNING);
|
||||||
if (ss != null) ss.close();
|
callback.transportDisabled();
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
} finally {
|
|
||||||
callback.transportDisabled();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String getIpPortString(InetSocketAddress a) {
|
String getIpPortString(InetSocketAddress a) {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import org.briarproject.bramble.api.system.LocationUtils;
|
|||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
import org.briarproject.bramble.util.IoUtils;
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@@ -303,8 +302,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
IoUtils.copyAndClose(in, out);
|
IoUtils.copyAndClose(in, out);
|
||||||
doneFile.createNewFile();
|
doneFile.createNewFile();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
tryToClose(in);
|
IoUtils.tryToClose(in, LOG, WARNING);
|
||||||
tryToClose(out);
|
IoUtils.tryToClose(out, LOG, WARNING);
|
||||||
throw new PluginException(e);
|
throw new PluginException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -341,22 +340,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
return getClass().getClassLoader().getResourceAsStream("torrc");
|
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) {
|
private void listFiles(File f) {
|
||||||
if (f.isDirectory()) {
|
if (f.isDirectory()) {
|
||||||
File[] children = f.listFiles();
|
File[] children = f.listFiles();
|
||||||
@@ -378,7 +361,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
} finally {
|
} 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) {
|
private void tryToClose(@Nullable ServerSocket ss) {
|
||||||
try {
|
IoUtils.tryToClose(ss, LOG, WARNING);
|
||||||
if (ss != null) ss.close();
|
callback.transportDisabled();
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
} finally {
|
|
||||||
callback.transportDisabled();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publishHiddenService(String port) {
|
private void publishHiddenService(String port) {
|
||||||
@@ -593,7 +571,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
LOG.info("Could not connect to " + scrubOnion(bestOnion)
|
LOG.info("Could not connect to " + scrubOnion(bestOnion)
|
||||||
+ ": " + e.toString());
|
+ ": " + e.toString());
|
||||||
}
|
}
|
||||||
tryToClose(s);
|
IoUtils.tryToClose(s, LOG, WARNING);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.contact.ContactManager;
|
|||||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
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.system.Clock;
|
||||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import org.briarproject.bramble.api.sync.GroupId;
|
|||||||
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
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.system.Clock;
|
||||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
|
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.reporting.DevReporter;
|
|||||||
import org.briarproject.bramble.util.IoUtils;
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
@@ -26,13 +25,12 @@ import java.net.Socket;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -66,7 +64,7 @@ class DevReporterImpl implements DevReporter, EventListener {
|
|||||||
s.setSoTimeout(SOCKET_TIMEOUT);
|
s.setSoTimeout(SOCKET_TIMEOUT);
|
||||||
return s;
|
return s;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
tryToClose(s);
|
tryToClose(s, LOG, WARNING);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,8 +86,7 @@ class DevReporterImpl implements DevReporter, EventListener {
|
|||||||
writer.append(armoured);
|
writer.append(armoured);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
} finally {
|
} finally {
|
||||||
if (writer != null)
|
tryToClose(writer, LOG, WARNING);
|
||||||
writer.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,27 +118,11 @@ class DevReporterImpl implements DevReporter, EventListener {
|
|||||||
f.delete();
|
f.delete();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.log(WARNING, "Failed to send reports", e);
|
LOG.log(WARNING, "Failed to send reports", e);
|
||||||
tryToClose(out);
|
tryToClose(out, LOG, WARNING);
|
||||||
tryToClose(in);
|
tryToClose(in, LOG, WARNING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG.info("Reports sent");
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,11 @@
|
|||||||
package org.briarproject.bramble.sync;
|
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.GroupFactory;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordReaderFactory;
|
import org.briarproject.bramble.api.sync.SyncRecordReaderFactory;
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordWriterFactory;
|
import org.briarproject.bramble.api.sync.SyncRecordWriterFactory;
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
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 javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@@ -26,29 +14,14 @@ import dagger.Provides;
|
|||||||
@Module
|
@Module
|
||||||
public class SyncModule {
|
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
|
@Provides
|
||||||
GroupFactory provideGroupFactory(CryptoComponent crypto) {
|
GroupFactory provideGroupFactory(GroupFactoryImpl groupFactory) {
|
||||||
return new GroupFactoryImpl(crypto);
|
return groupFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
MessageFactory provideMessageFactory(CryptoComponent crypto) {
|
MessageFactory provideMessageFactory(MessageFactoryImpl messageFactory) {
|
||||||
return new MessageFactoryImpl(crypto);
|
return messageFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -65,30 +38,8 @@ public class SyncModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SyncSessionFactory provideSyncSessionFactory(DatabaseComponent db,
|
SyncSessionFactory provideSyncSessionFactory(
|
||||||
@DatabaseExecutor Executor dbExecutor, EventBus eventBus,
|
SyncSessionFactoryImpl syncSessionFactory) {
|
||||||
Clock clock, SyncRecordReaderFactory recordReaderFactory,
|
return syncSessionFactory;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.bramble.sync;
|
package org.briarproject.bramble.sync.validation;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
@@ -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.Pair;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
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.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageContext;
|
import org.briarproject.bramble.api.sync.MessageContext;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
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.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 org.briarproject.bramble.api.versioning.ClientMajorVersion;
|
||||||
|
|
||||||
import java.util.Collection;
|
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.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
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.api.sync.ValidationManager.State.INVALID;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@@ -166,9 +169,9 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
// Check if message is still pending
|
// Check if message is still pending
|
||||||
if (db.getMessageState(txn, id) == PENDING) {
|
if (db.getMessageState(txn, id) == PENDING) {
|
||||||
// Check if dependencies are valid and delivered
|
// Check if dependencies are valid and delivered
|
||||||
Map<MessageId, State> states =
|
Map<MessageId, MessageState> states =
|
||||||
db.getMessageDependencies(txn, id);
|
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() == INVALID) anyInvalid = true;
|
||||||
if (e.getValue() != DELIVERED) allDelivered = false;
|
if (e.getValue() != DELIVERED) allDelivered = false;
|
||||||
}
|
}
|
||||||
@@ -256,9 +259,9 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
if (!dependencies.isEmpty()) {
|
if (!dependencies.isEmpty()) {
|
||||||
db.addMessageDependencies(txn, m, dependencies);
|
db.addMessageDependencies(txn, m, dependencies);
|
||||||
// Check if dependencies are valid and delivered
|
// Check if dependencies are valid and delivered
|
||||||
Map<MessageId, State> states =
|
Map<MessageId, MessageState> states =
|
||||||
db.getMessageDependencies(txn, id);
|
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() == INVALID) anyInvalid = true;
|
||||||
if (e.getValue() != DELIVERED) allDelivered = false;
|
if (e.getValue() != DELIVERED) allDelivered = false;
|
||||||
}
|
}
|
||||||
@@ -322,8 +325,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private void addPendingDependents(Transaction txn, MessageId m,
|
private void addPendingDependents(Transaction txn, MessageId m,
|
||||||
Queue<MessageId> pending) throws DbException {
|
Queue<MessageId> pending) throws DbException {
|
||||||
Map<MessageId, State> states = db.getMessageDependents(txn, m);
|
Map<MessageId, MessageState> states = db.getMessageDependents(txn, m);
|
||||||
for (Entry<MessageId, State> e : states.entrySet()) {
|
for (Entry<MessageId, MessageState> e : states.entrySet()) {
|
||||||
if (e.getValue() == PENDING) pending.add(e.getKey());
|
if (e.getValue() == PENDING) pending.add(e.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -411,8 +414,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private void addDependentsToInvalidate(Transaction txn,
|
private void addDependentsToInvalidate(Transaction txn,
|
||||||
MessageId m, Queue<MessageId> invalidate) throws DbException {
|
MessageId m, Queue<MessageId> invalidate) throws DbException {
|
||||||
Map<MessageId, State> states = db.getMessageDependents(txn, m);
|
Map<MessageId, MessageState> states = db.getMessageDependents(txn, m);
|
||||||
for (Entry<MessageId, State> e : states.entrySet()) {
|
for (Entry<MessageId, MessageState> e : states.entrySet()) {
|
||||||
if (e.getValue() != INVALID) invalidate.add(e.getKey());
|
if (e.getValue() != INVALID) invalidate.add(e.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,32 +13,33 @@ import java.util.logging.Logger;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
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.util.LogUtils.logException;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
|
class UnixSecureRandomProvider extends AbstractSecureRandomProvider {
|
||||||
|
|
||||||
private static final Logger LOG =
|
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 static final File RANDOM_DEVICE = new File("/dev/urandom");
|
||||||
|
|
||||||
private final AtomicBoolean seeded = new AtomicBoolean(false);
|
private final AtomicBoolean seeded = new AtomicBoolean(false);
|
||||||
private final File outputDevice;
|
private final File outputDevice;
|
||||||
|
|
||||||
LinuxSecureRandomProvider() {
|
UnixSecureRandomProvider() {
|
||||||
this(RANDOM_DEVICE);
|
this(RANDOM_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
LinuxSecureRandomProvider(File outputDevice) {
|
UnixSecureRandomProvider(File outputDevice) {
|
||||||
this.outputDevice = outputDevice;
|
this.outputDevice = outputDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Provider getProvider() {
|
public Provider getProvider() {
|
||||||
if (!seeded.getAndSet(true)) writeSeed();
|
if (!seeded.getAndSet(true)) writeSeed();
|
||||||
return new LinuxProvider();
|
return new UnixProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeSeed() {
|
protected void writeSeed() {
|
||||||
@@ -55,15 +56,15 @@ class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
// 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() {
|
private UnixProvider() {
|
||||||
super("LinuxPRNG", 1.1, "A Linux-specific PRNG using /dev/urandom");
|
super("UnixPRNG", 1.0, "A Unix-specific PRNG using /dev/urandom");
|
||||||
// Although /dev/urandom is not a SHA-1 PRNG, some callers
|
// Although /dev/urandom is not a SHA-1 PRNG, some callers
|
||||||
// explicitly request a SHA1PRNG SecureRandom and we need to
|
// explicitly request a SHA1PRNG SecureRandom and we need to
|
||||||
// prevent them from getting the default implementation whose
|
// prevent them from getting the default implementation whose
|
||||||
// output may have low entropy.
|
// output may have low entropy.
|
||||||
put("SecureRandom.SHA1PRNG", LinuxSecureRandomSpi.class.getName());
|
put("SecureRandom.SHA1PRNG", UnixSecureRandomSpi.class.getName());
|
||||||
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
|
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,22 +10,24 @@ import java.security.SecureRandomSpi;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
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.util.LogUtils.logException;
|
||||||
|
|
||||||
public class LinuxSecureRandomSpi extends SecureRandomSpi {
|
public class UnixSecureRandomSpi extends SecureRandomSpi {
|
||||||
|
|
||||||
private static final Logger LOG =
|
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 static final File RANDOM_DEVICE = new File("/dev/urandom");
|
||||||
|
|
||||||
private final File inputDevice, outputDevice;
|
private final File inputDevice, outputDevice;
|
||||||
|
|
||||||
public LinuxSecureRandomSpi() {
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public UnixSecureRandomSpi() {
|
||||||
this(RANDOM_DEVICE, RANDOM_DEVICE);
|
this(RANDOM_DEVICE, RANDOM_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
LinuxSecureRandomSpi(File inputDevice, File outputDevice) {
|
UnixSecureRandomSpi(File inputDevice, File outputDevice) {
|
||||||
this.inputDevice = inputDevice;
|
this.inputDevice = inputDevice;
|
||||||
this.outputDevice = outputDevice;
|
this.outputDevice = outputDevice;
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ import org.briarproject.bramble.api.sync.GroupId;
|
|||||||
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
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.system.Clock;
|
||||||
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
|
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
|
||||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import org.briarproject.bramble.api.client.ClientHelper;
|
|||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
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.system.Clock;
|
||||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||||
|
|
||||||
|
|||||||
@@ -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.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
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.SyncConstants.MAX_MESSAGE_LENGTH;
|
||||||
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.api.sync.ValidationManager.State.UNKNOWN;
|
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.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
|
import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import org.briarproject.bramble.api.sync.Group;
|
|||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
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.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.UTest;
|
import org.briarproject.bramble.test.UTest;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@@ -33,7 +33,7 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import static java.util.logging.Level.OFF;
|
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.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.deleteTestDirectory;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
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++) {
|
for (int k = 0; k < MESSAGES_PER_GROUP; k++) {
|
||||||
Message m = getMessage(g.getId());
|
Message m = getMessage(g.getId());
|
||||||
messages.add(m);
|
messages.add(m);
|
||||||
State state = State.fromValue(random.nextInt(4));
|
MessageState state =
|
||||||
|
MessageState.fromValue(random.nextInt(4));
|
||||||
boolean shared = random.nextBoolean();
|
boolean shared = random.nextBoolean();
|
||||||
ContactId sender = random.nextBoolean() ? c : null;
|
ContactId sender = random.nextBoolean() ? c : null;
|
||||||
db.addMessage(txn, m, state, shared, sender);
|
db.addMessage(txn, m, state, shared, sender);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import org.briarproject.bramble.api.sync.Message;
|
|||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
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.system.Clock;
|
||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
import org.briarproject.bramble.api.transport.KeySet;
|
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.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
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.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||||
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.api.sync.ValidationManager.State.INVALID;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
|
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.DB_SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
|
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.MAX_COMPACTION_INTERVAL_MS;
|
||||||
@@ -1309,7 +1309,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.addMessageDependency(txn, message1, messageId3, PENDING);
|
db.addMessageDependency(txn, message1, messageId3, PENDING);
|
||||||
db.addMessageDependency(txn, message2, messageId4, INVALID);
|
db.addMessageDependency(txn, message2, messageId4, INVALID);
|
||||||
|
|
||||||
Map<MessageId, State> dependencies;
|
Map<MessageId, MessageState> dependencies;
|
||||||
|
|
||||||
// Retrieve dependencies for root
|
// Retrieve dependencies for root
|
||||||
dependencies = db.getMessageDependencies(txn, messageId);
|
dependencies = db.getMessageDependencies(txn, messageId);
|
||||||
@@ -1333,7 +1333,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
dependencies = db.getMessageDependencies(txn, messageId4);
|
dependencies = db.getMessageDependencies(txn, messageId4);
|
||||||
assertEquals(0, dependencies.size());
|
assertEquals(0, dependencies.size());
|
||||||
|
|
||||||
Map<MessageId, State> dependents;
|
Map<MessageId, MessageState> dependents;
|
||||||
|
|
||||||
// Root message does not have dependents
|
// Root message does not have dependents
|
||||||
dependents = db.getMessageDependents(txn, messageId);
|
dependents = db.getMessageDependents(txn, messageId);
|
||||||
@@ -1408,7 +1408,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.addMessageDependency(txn, message, messageId3, PENDING);
|
db.addMessageDependency(txn, message, messageId3, PENDING);
|
||||||
|
|
||||||
// Retrieve the dependencies for the root
|
// Retrieve the dependencies for the root
|
||||||
Map<MessageId, State> dependencies;
|
Map<MessageId, MessageState> dependencies;
|
||||||
dependencies = db.getMessageDependencies(txn, messageId);
|
dependencies = db.getMessageDependencies(txn, messageId);
|
||||||
|
|
||||||
// The cross-group dependency should have state UNKNOWN
|
// The cross-group dependency should have state UNKNOWN
|
||||||
@@ -1421,7 +1421,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
assertEquals(DELIVERED, dependencies.get(messageId3));
|
assertEquals(DELIVERED, dependencies.get(messageId3));
|
||||||
|
|
||||||
// Retrieve the dependents for the message in the second group
|
// Retrieve the dependents for the message in the second group
|
||||||
Map<MessageId, State> dependents;
|
Map<MessageId, MessageState> dependents;
|
||||||
dependents = db.getMessageDependents(txn, messageId1);
|
dependents = db.getMessageDependents(txn, messageId1);
|
||||||
|
|
||||||
// The cross-group dependent should be excluded
|
// The cross-group dependent should be excluded
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package org.briarproject.bramble.io;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.io.BlockSource;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.jmock.lib.concurrent.Synchroniser;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import static java.util.concurrent.Executors.newSingleThreadExecutor;
|
||||||
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_BLOCK_LENGTH;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.spongycastle.util.Arrays.copyOfRange;
|
||||||
|
|
||||||
|
public class BlockSourceInputStreamTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private static final int MAX_DATA_BYTES = 1_000_000;
|
||||||
|
private static final int READ_BUFFER_BYTES = 4 * 1024;
|
||||||
|
|
||||||
|
private final BlockSource blockSource;
|
||||||
|
|
||||||
|
private final Random random = new Random();
|
||||||
|
private final Executor executor = newSingleThreadExecutor();
|
||||||
|
private final MessageId messageId = new MessageId(getRandomId());
|
||||||
|
|
||||||
|
public BlockSourceInputStreamTest() {
|
||||||
|
context.setThreadingPolicy(new Synchroniser());
|
||||||
|
blockSource = context.mock(BlockSource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadSingleBytes() throws IOException {
|
||||||
|
byte[] data = createRandomData();
|
||||||
|
BlockSource source = new ByteArrayBlockSource(data, MAX_BLOCK_LENGTH);
|
||||||
|
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
||||||
|
source, messageId);
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
//noinspection ForLoopReplaceableByForEach
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
int read = in.read();
|
||||||
|
assertNotEquals(-1, read);
|
||||||
|
out.write(read);
|
||||||
|
}
|
||||||
|
assertEquals(-1, in.read());
|
||||||
|
in.close();
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
assertArrayEquals(data, out.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadByteArrays() throws IOException {
|
||||||
|
byte[] data = createRandomData();
|
||||||
|
BlockSource source = new ByteArrayBlockSource(data, MAX_BLOCK_LENGTH);
|
||||||
|
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
||||||
|
source, messageId);
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
byte[] buf = new byte[READ_BUFFER_BYTES];
|
||||||
|
int dataOffset = 0;
|
||||||
|
while (dataOffset < data.length) {
|
||||||
|
int length = Math.min(random.nextInt(buf.length) + 1,
|
||||||
|
data.length - dataOffset);
|
||||||
|
int bufOffset = 0;
|
||||||
|
if (length < buf.length)
|
||||||
|
bufOffset = random.nextInt(buf.length - length);
|
||||||
|
int read = in.read(buf, bufOffset, length);
|
||||||
|
assertNotEquals(-1, read);
|
||||||
|
out.write(buf, bufOffset, read);
|
||||||
|
dataOffset += read;
|
||||||
|
}
|
||||||
|
assertEquals(-1, in.read(buf, 0, 0));
|
||||||
|
in.close();
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
assertArrayEquals(data, out.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testDbExceptionFromGetBlockCountIsRethrown() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(blockSource).getBlockCount(messageId);
|
||||||
|
will(throwException(new DbException()));
|
||||||
|
}});
|
||||||
|
|
||||||
|
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
||||||
|
blockSource, messageId);
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
in.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testDbExceptionFromGetBlockIsRethrown() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(blockSource).getBlockCount(messageId);
|
||||||
|
will(returnValue(1));
|
||||||
|
oneOf(blockSource).getBlock(messageId, 0);
|
||||||
|
will(throwException(new DbException()));
|
||||||
|
}});
|
||||||
|
|
||||||
|
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
||||||
|
blockSource, messageId);
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
in.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadFullBlockAtEndOfMessage() throws Exception {
|
||||||
|
testReadBlockAtEndOfMessage(MAX_BLOCK_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadPartialBlockAtEndOfMessage() throws Exception {
|
||||||
|
testReadBlockAtEndOfMessage(MAX_BLOCK_LENGTH - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testReadBlockAtEndOfMessage(int blockLength) throws Exception {
|
||||||
|
byte[] block = new byte[blockLength];
|
||||||
|
random.nextBytes(block);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(blockSource).getBlockCount(messageId);
|
||||||
|
will(returnValue(1));
|
||||||
|
oneOf(blockSource).getBlock(messageId, 0);
|
||||||
|
will(returnValue(block));
|
||||||
|
}});
|
||||||
|
|
||||||
|
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
||||||
|
blockSource, messageId);
|
||||||
|
byte[] buf = new byte[MAX_BLOCK_LENGTH * 2];
|
||||||
|
assertEquals(block.length, in.read(buf, 0, buf.length));
|
||||||
|
assertArrayEquals(block, copyOfRange(buf, 0, block.length));
|
||||||
|
assertEquals(-1, in.read(buf, 0, buf.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] createRandomData() {
|
||||||
|
int length = random.nextInt(MAX_DATA_BYTES) + 1;
|
||||||
|
byte[] data = new byte[length];
|
||||||
|
random.nextBytes(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.briarproject.bramble.io;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.io.BlockSource;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
import static java.lang.System.arraycopy;
|
||||||
|
|
||||||
|
class ByteArrayBlockSource implements BlockSource {
|
||||||
|
|
||||||
|
private final byte[] data;
|
||||||
|
private final int blockBytes;
|
||||||
|
|
||||||
|
ByteArrayBlockSource(byte[] data, int blockBytes) {
|
||||||
|
this.data = data;
|
||||||
|
this.blockBytes = blockBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBlockCount(MessageId m) {
|
||||||
|
return (data.length + blockBytes - 1) / blockBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBlock(MessageId m, int blockNumber) {
|
||||||
|
int offset = blockNumber * blockBytes;
|
||||||
|
if (offset >= data.length) throw new IllegalArgumentException();
|
||||||
|
int length = Math.min(blockBytes, data.length - offset);
|
||||||
|
byte[] block = new byte[length];
|
||||||
|
arraycopy(data, offset, block, 0, length);
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.sync;
|
|||||||
|
|
||||||
import org.briarproject.bramble.crypto.CryptoModule;
|
import org.briarproject.bramble.crypto.CryptoModule;
|
||||||
import org.briarproject.bramble.record.RecordModule;
|
import org.briarproject.bramble.record.RecordModule;
|
||||||
|
import org.briarproject.bramble.sync.validation.ValidationModule;
|
||||||
import org.briarproject.bramble.system.SystemModule;
|
import org.briarproject.bramble.system.SystemModule;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||||
import org.briarproject.bramble.transport.TransportModule;
|
import org.briarproject.bramble.transport.TransportModule;
|
||||||
@@ -17,6 +18,7 @@ import dagger.Component;
|
|||||||
RecordModule.class,
|
RecordModule.class,
|
||||||
SyncModule.class,
|
SyncModule.class,
|
||||||
SystemModule.class,
|
SystemModule.class,
|
||||||
|
ValidationModule.class,
|
||||||
TransportModule.class
|
TransportModule.class
|
||||||
})
|
})
|
||||||
interface SyncIntegrationTestComponent {
|
interface SyncIntegrationTestComponent {
|
||||||
|
|||||||
@@ -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.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
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.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageContext;
|
import org.briarproject.bramble.api.sync.MessageContext;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
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.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.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
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.emptyMap;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
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.api.sync.ValidationManager.State.INVALID;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
|
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.getClientId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
@@ -559,7 +559,7 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
|
|||||||
public void testRecursiveInvalidation() throws Exception {
|
public void testRecursiveInvalidation() throws Exception {
|
||||||
MessageId messageId3 = new MessageId(getRandomId());
|
MessageId messageId3 = new MessageId(getRandomId());
|
||||||
MessageId messageId4 = 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(messageId1, PENDING);
|
||||||
twoDependents.put(messageId2, PENDING);
|
twoDependents.put(messageId2, PENDING);
|
||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
@@ -643,10 +643,10 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
|
|||||||
Message message4 = getMessage(groupId);
|
Message message4 = getMessage(groupId);
|
||||||
MessageId messageId3 = message3.getId();
|
MessageId messageId3 = message3.getId();
|
||||||
MessageId messageId4 = message4.getId();
|
MessageId messageId4 = message4.getId();
|
||||||
Map<MessageId, State> twoDependents = new LinkedHashMap<>();
|
Map<MessageId, MessageState> twoDependents = new LinkedHashMap<>();
|
||||||
twoDependents.put(messageId1, PENDING);
|
twoDependents.put(messageId1, PENDING);
|
||||||
twoDependents.put(messageId2, PENDING);
|
twoDependents.put(messageId2, PENDING);
|
||||||
Map<MessageId, State> twoDependencies = new LinkedHashMap<>();
|
Map<MessageId, MessageState> twoDependencies = new LinkedHashMap<>();
|
||||||
twoDependencies.put(messageId1, DELIVERED);
|
twoDependencies.put(messageId1, DELIVERED);
|
||||||
twoDependencies.put(messageId2, DELIVERED);
|
twoDependencies.put(messageId2, DELIVERED);
|
||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
@@ -765,7 +765,7 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnlyReadyPendingDependentsGetDelivered() throws Exception {
|
public void testOnlyReadyPendingDependentsGetDelivered() throws Exception {
|
||||||
Map<MessageId, State> twoDependencies = new LinkedHashMap<>();
|
Map<MessageId, MessageState> twoDependencies = new LinkedHashMap<>();
|
||||||
twoDependencies.put(messageId, DELIVERED);
|
twoDependencies.put(messageId, DELIVERED);
|
||||||
twoDependencies.put(messageId2, UNKNOWN);
|
twoDependencies.put(messageId2, UNKNOWN);
|
||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
package org.briarproject.bramble.system;
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
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.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -10,48 +8,50 @@ import org.junit.Test;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.security.Provider;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
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
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
testDir.mkdirs();
|
assumeTrue(isLinux() || isMac());
|
||||||
|
assertTrue(testDir.mkdirs());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetProviderWritesToRandomDeviceOnFirstCall()
|
public void testGetProviderWritesToRandomDeviceOnFirstCall()
|
||||||
throws Exception {
|
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
|
// Redirect the provider's output to a file
|
||||||
File urandom = new File(testDir, "urandom");
|
File urandom = new File(testDir, "urandom");
|
||||||
urandom.delete();
|
if (urandom.exists()) assertTrue(urandom.delete());
|
||||||
assertTrue(urandom.createNewFile());
|
assertTrue(urandom.createNewFile());
|
||||||
assertEquals(0, urandom.length());
|
assertEquals(0, urandom.length());
|
||||||
LinuxSecureRandomProvider p = new LinuxSecureRandomProvider(urandom);
|
UnixSecureRandomProvider p = new UnixSecureRandomProvider(urandom);
|
||||||
// Getting a provider should write entropy to the file
|
// Getting a provider should write entropy to the file
|
||||||
Provider provider = p.getProvider();
|
Provider provider = p.getProvider();
|
||||||
assertNotNull(provider);
|
assertNotNull(provider);
|
||||||
assertEquals("LinuxPRNG", provider.getName());
|
assertEquals("UnixPRNG", provider.getName());
|
||||||
// There should be at least 16 bytes from the clock, 8 from the runtime
|
// There should be at least 16 bytes from the clock, 8 from the runtime
|
||||||
long length = urandom.length();
|
long length = urandom.length();
|
||||||
assertTrue(length >= 24);
|
assertTrue(length >= 24);
|
||||||
// Getting another provider should not write to the file again
|
// Getting another provider should not write to the file again
|
||||||
provider = p.getProvider();
|
provider = p.getProvider();
|
||||||
assertNotNull(provider);
|
assertNotNull(provider);
|
||||||
assertEquals("LinuxPRNG", provider.getName());
|
assertEquals("UnixPRNG", provider.getName());
|
||||||
assertEquals(length, urandom.length());
|
assertEquals(length, urandom.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
TestUtils.deleteTestDirectory(testDir);
|
deleteTestDirectory(testDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.Bytes;
|
|||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.briarproject.bramble.util.IoUtils;
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
import org.briarproject.bramble.util.OsUtils;
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -15,31 +14,33 @@ import java.io.FileOutputStream;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
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.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
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 File RANDOM_DEVICE = new File("/dev/urandom");
|
||||||
private static final int SEED_BYTES = 32;
|
private static final int SEED_BYTES = 32;
|
||||||
|
|
||||||
private final File testDir = TestUtils.getTestDirectory();
|
private final File testDir = getTestDirectory();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
testDir.mkdirs();
|
assumeTrue(isLinux() || isMac());
|
||||||
|
assertTrue(testDir.mkdirs());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSeedsAreDistinct() {
|
public void testSeedsAreDistinct() {
|
||||||
if (!(OsUtils.isLinux())) {
|
|
||||||
System.err.println("WARNING: Skipping test, can't run on this OS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Set<Bytes> seeds = new HashSet<>();
|
Set<Bytes> seeds = new HashSet<>();
|
||||||
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi();
|
UnixSecureRandomSpi engine = new UnixSecureRandomSpi();
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
byte[] seed = engine.engineGenerateSeed(SEED_BYTES);
|
byte[] seed = engine.engineGenerateSeed(SEED_BYTES);
|
||||||
assertEquals(SEED_BYTES, seed.length);
|
assertEquals(SEED_BYTES, seed.length);
|
||||||
@@ -49,19 +50,15 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEngineSetSeedWritesToRandomDevice() throws Exception {
|
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
|
// Redirect the engine's output to a file
|
||||||
File urandom = new File(testDir, "urandom");
|
File urandom = new File(testDir, "urandom");
|
||||||
urandom.delete();
|
if (urandom.exists()) assertTrue(urandom.delete());
|
||||||
assertTrue(urandom.createNewFile());
|
assertTrue(urandom.createNewFile());
|
||||||
assertEquals(0, urandom.length());
|
assertEquals(0, urandom.length());
|
||||||
// Generate a seed
|
// Generate a seed
|
||||||
byte[] seed = TestUtils.getRandomBytes(SEED_BYTES);
|
byte[] seed = TestUtils.getRandomBytes(SEED_BYTES);
|
||||||
// Check that the engine writes the seed to the file
|
// Check that the engine writes the seed to the file
|
||||||
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(RANDOM_DEVICE,
|
UnixSecureRandomSpi engine = new UnixSecureRandomSpi(RANDOM_DEVICE,
|
||||||
urandom);
|
urandom);
|
||||||
engine.engineSetSeed(seed);
|
engine.engineSetSeed(seed);
|
||||||
assertEquals(SEED_BYTES, urandom.length());
|
assertEquals(SEED_BYTES, urandom.length());
|
||||||
@@ -74,15 +71,11 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEngineNextBytesReadsFromRandomDevice() throws Exception {
|
public void testEngineNextBytesReadsFromRandomDevice() throws Exception {
|
||||||
if (!(OsUtils.isLinux())) {
|
|
||||||
System.err.println("WARNING: Skipping test, can't run on this OS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Generate some entropy
|
// Generate some entropy
|
||||||
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
|
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
|
||||||
// Write the entropy to a file
|
// Write the entropy to a file
|
||||||
File urandom = new File(testDir, "urandom");
|
File urandom = new File(testDir, "urandom");
|
||||||
urandom.delete();
|
if (urandom.exists()) assertTrue(urandom.delete());
|
||||||
FileOutputStream out = new FileOutputStream(urandom);
|
FileOutputStream out = new FileOutputStream(urandom);
|
||||||
out.write(entropy);
|
out.write(entropy);
|
||||||
out.flush();
|
out.flush();
|
||||||
@@ -90,7 +83,7 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
|
|||||||
assertTrue(urandom.exists());
|
assertTrue(urandom.exists());
|
||||||
assertEquals(SEED_BYTES, urandom.length());
|
assertEquals(SEED_BYTES, urandom.length());
|
||||||
// Check that the engine reads from the file
|
// Check that the engine reads from the file
|
||||||
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
|
UnixSecureRandomSpi engine = new UnixSecureRandomSpi(urandom,
|
||||||
RANDOM_DEVICE);
|
RANDOM_DEVICE);
|
||||||
byte[] b = new byte[SEED_BYTES];
|
byte[] b = new byte[SEED_BYTES];
|
||||||
engine.engineNextBytes(b);
|
engine.engineNextBytes(b);
|
||||||
@@ -99,15 +92,11 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEngineGenerateSeedReadsFromRandomDevice() throws Exception {
|
public void testEngineGenerateSeedReadsFromRandomDevice() throws Exception {
|
||||||
if (!(OsUtils.isLinux())) {
|
|
||||||
System.err.println("WARNING: Skipping test, can't run on this OS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Generate some entropy
|
// Generate some entropy
|
||||||
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
|
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
|
||||||
// Write the entropy to a file
|
// Write the entropy to a file
|
||||||
File urandom = new File(testDir, "urandom");
|
File urandom = new File(testDir, "urandom");
|
||||||
urandom.delete();
|
if (urandom.exists()) assertTrue(urandom.delete());
|
||||||
FileOutputStream out = new FileOutputStream(urandom);
|
FileOutputStream out = new FileOutputStream(urandom);
|
||||||
out.write(entropy);
|
out.write(entropy);
|
||||||
out.flush();
|
out.flush();
|
||||||
@@ -115,7 +104,7 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
|
|||||||
assertTrue(urandom.exists());
|
assertTrue(urandom.exists());
|
||||||
assertEquals(SEED_BYTES, urandom.length());
|
assertEquals(SEED_BYTES, urandom.length());
|
||||||
// Check that the engine reads from the file
|
// Check that the engine reads from the file
|
||||||
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
|
UnixSecureRandomSpi engine = new UnixSecureRandomSpi(urandom,
|
||||||
RANDOM_DEVICE);
|
RANDOM_DEVICE);
|
||||||
byte[] b = engine.engineGenerateSeed(SEED_BYTES);
|
byte[] b = engine.engineGenerateSeed(SEED_BYTES);
|
||||||
assertArrayEquals(entropy, b);
|
assertArrayEquals(entropy, b);
|
||||||
@@ -123,6 +112,6 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
TestUtils.deleteTestDirectory(testDir);
|
deleteTestDirectory(testDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,21 @@
|
|||||||
package org.briarproject.bramble.lifecycle;
|
package org.briarproject.bramble.lifecycle;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
||||||
import org.briarproject.bramble.util.OsUtils;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.util.OsUtils.isWindows;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class DesktopLifecycleModule extends LifecycleModule {
|
public class DesktopLifecycleModule extends LifecycleModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
ShutdownManager provideDesktopShutdownManager() {
|
ShutdownManager provideDesktopShutdownManager() {
|
||||||
if (OsUtils.isWindows()) return new WindowsShutdownManagerImpl();
|
if (isWindows()) return new WindowsShutdownManagerImpl();
|
||||||
else return new ShutdownManagerImpl();
|
else return new ShutdownManagerImpl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import com.sun.jna.win32.W32APIFunctionMapper;
|
|||||||
import com.sun.jna.win32.W32APITypeMapper;
|
import com.sun.jna.win32.W32APITypeMapper;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.util.OsUtils;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
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 com.sun.jna.Library.OPTION_TYPE_MAPPER;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.OsUtils.isWindows;
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -71,7 +71,7 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
|
|||||||
|
|
||||||
// Locking: lock
|
// Locking: lock
|
||||||
private void initialise() {
|
private void initialise() {
|
||||||
if (OsUtils.isWindows()) {
|
if (isWindows()) {
|
||||||
new EventLoop().start();
|
new EventLoop().start();
|
||||||
} else {
|
} else {
|
||||||
LOG.warning("Windows shutdown manager used on non-Windows OS");
|
LOG.warning("Windows shutdown manager used on non-Windows OS");
|
||||||
@@ -111,7 +111,7 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
|
|||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
// Load user32.dll
|
// Load user32.dll
|
||||||
User32 user32 = (User32) Native.loadLibrary("user32",
|
User32 user32 = Native.loadLibrary("user32",
|
||||||
User32.class, options);
|
User32.class, options);
|
||||||
// Create a callback to handle the WM_QUERYENDSESSION message
|
// Create a callback to handle the WM_QUERYENDSESSION message
|
||||||
WindowProc proc = (hwnd, msg, wp, lp) -> {
|
WindowProc proc = (hwnd, msg, wp, lp) -> {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.system.LocationUtils;
|
|||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.security.CodeSource;
|
import java.security.CodeSource;
|
||||||
@@ -19,9 +18,9 @@ import java.util.concurrent.Executor;
|
|||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class LinuxTorPlugin extends TorPlugin {
|
abstract class JavaTorPlugin extends TorPlugin {
|
||||||
|
|
||||||
LinuxTorPlugin(Executor ioExecutor, NetworkManager networkManager,
|
JavaTorPlugin(Executor ioExecutor, NetworkManager networkManager,
|
||||||
LocationUtils locationUtils, SocketFactory torSocketFactory,
|
LocationUtils locationUtils, SocketFactory torSocketFactory,
|
||||||
Clock clock, ResourceProvider resourceProvider,
|
Clock clock, ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
@@ -34,17 +33,6 @@ class LinuxTorPlugin extends TorPlugin {
|
|||||||
torDirectory);
|
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
|
@Override
|
||||||
protected long getLastUpdateTime() {
|
protected long getLastUpdateTime() {
|
||||||
CodeSource codeSource =
|
CodeSource codeSource =
|
||||||
@@ -58,5 +46,4 @@ class LinuxTorPlugin extends TorPlugin {
|
|||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,14 +22,15 @@ import java.util.logging.Logger;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.OsUtils.isLinux;
|
import static org.briarproject.bramble.util.OsUtils.isLinux;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class LinuxTorPluginFactory implements DuplexPluginFactory {
|
public class UnixTorPluginFactory implements DuplexPluginFactory {
|
||||||
|
|
||||||
private static final Logger LOG =
|
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_LATENCY = 30 * 1000; // 30 seconds
|
||||||
private static final int MAX_IDLE_TIME = 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 Clock clock;
|
||||||
private final File torDirectory;
|
private final File torDirectory;
|
||||||
|
|
||||||
public LinuxTorPluginFactory(Executor ioExecutor,
|
public UnixTorPluginFactory(Executor ioExecutor,
|
||||||
NetworkManager networkManager, LocationUtils locationUtils,
|
NetworkManager networkManager, LocationUtils locationUtils,
|
||||||
EventBus eventBus, SocketFactory torSocketFactory,
|
EventBus eventBus, SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory, ResourceProvider resourceProvider,
|
BackoffFactory backoffFactory, ResourceProvider resourceProvider,
|
||||||
@@ -95,7 +96,7 @@ public class LinuxTorPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
LinuxTorPlugin plugin = new LinuxTorPlugin(ioExecutor, networkManager,
|
UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, networkManager,
|
||||||
locationUtils, torSocketFactory, clock, resourceProvider,
|
locationUtils, torSocketFactory, clock, resourceProvider,
|
||||||
circumventionProvider, batteryManager, backoff, callback,
|
circumventionProvider, batteryManager, backoff, callback,
|
||||||
architecture, MAX_LATENCY, MAX_IDLE_TIME, torDirectory);
|
architecture, MAX_LATENCY, MAX_IDLE_TIME, torDirectory);
|
||||||
@@ -1,19 +1,24 @@
|
|||||||
package org.briarproject.bramble.system;
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.util.OsUtils;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.util.OsUtils.isLinux;
|
||||||
|
import static org.briarproject.bramble.util.OsUtils.isMac;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class DesktopSecureRandomModule {
|
public class DesktopSecureRandomModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SecureRandomProvider provideSecureRandomProvider() {
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public class BridgeTest extends BrambleTestCase {
|
|||||||
private final File torDir = getTestDirectory();
|
private final File torDir = getTestDirectory();
|
||||||
private final String bridge;
|
private final String bridge;
|
||||||
|
|
||||||
private LinuxTorPluginFactory factory;
|
private UnixTorPluginFactory factory;
|
||||||
|
|
||||||
public BridgeTest(String bridge) {
|
public BridgeTest(String bridge) {
|
||||||
this.bridge = bridge;
|
this.bridge = bridge;
|
||||||
@@ -108,7 +108,7 @@ public class BridgeTest extends BrambleTestCase {
|
|||||||
return singletonList(bridge);
|
return singletonList(bridge);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
factory = new LinuxTorPluginFactory(ioExecutor, networkManager,
|
factory = new UnixTorPluginFactory(ioExecutor, networkManager,
|
||||||
locationUtils, eventBus, torSocketFactory, backoffFactory,
|
locationUtils, eventBus, torSocketFactory, backoffFactory,
|
||||||
resourceProvider, bridgeProvider, batteryManager, clock,
|
resourceProvider, bridgeProvider, batteryManager, clock,
|
||||||
torDir);
|
torDir);
|
||||||
@@ -124,7 +124,7 @@ public class BridgeTest extends BrambleTestCase {
|
|||||||
DuplexPlugin duplexPlugin =
|
DuplexPlugin duplexPlugin =
|
||||||
factory.createPlugin(new TorPluginCallBack());
|
factory.createPlugin(new TorPluginCallBack());
|
||||||
assertNotNull(duplexPlugin);
|
assertNotNull(duplexPlugin);
|
||||||
LinuxTorPlugin plugin = (LinuxTorPlugin) duplexPlugin;
|
UnixTorPlugin plugin = (UnixTorPlugin) duplexPlugin;
|
||||||
|
|
||||||
LOG.warning("Testing " + bridge);
|
LOG.warning("Testing " + bridge);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ dependencies {
|
|||||||
}
|
}
|
||||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||||
implementation "com.android.support:support-annotations:$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 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||||
implementation "android.arch.lifecycle:extensions:1.1.1"
|
implementation "android.arch.lifecycle:extensions:1.1.1"
|
||||||
|
|
||||||
@@ -117,8 +118,15 @@ dependencies {
|
|||||||
implementation 'com.google.zxing:core:3.3.3'
|
implementation 'com.google.zxing:core:3.3.3'
|
||||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.12.4'
|
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.12.4'
|
||||||
implementation 'com.vanniktech:emoji-google:0.5.1'
|
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.google.dagger:dagger-compiler:2.19'
|
||||||
|
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
|
||||||
|
|
||||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||||
|
|
||||||
|
|||||||
@@ -30,3 +30,6 @@
|
|||||||
|
|
||||||
# Emoji
|
# Emoji
|
||||||
-keep class com.vanniktech.emoji.**
|
-keep class com.vanniktech.emoji.**
|
||||||
|
|
||||||
|
# Glide
|
||||||
|
-dontwarn com.bumptech.glide.load.engine.cache.DiskLruCacheWrapper
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.BrambleAndroidModule;
|
||||||
import org.briarproject.bramble.BrambleCoreModule;
|
import org.briarproject.bramble.BrambleCoreModule;
|
||||||
import org.briarproject.briar.BriarCoreModule;
|
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
|
// We need to load the eager singletons directly after making the
|
||||||
// dependency graphs
|
// dependency graphs
|
||||||
BrambleCoreModule.initEagerSingletons(component);
|
BrambleCoreModule.initEagerSingletons(component);
|
||||||
|
BrambleAndroidModule.initEagerSingletons(component);
|
||||||
BriarCoreModule.initEagerSingletons(component);
|
BriarCoreModule.initEagerSingletons(component);
|
||||||
AndroidEagerSingletons.initEagerSingletons(component);
|
AndroidEagerSingletons.initEagerSingletons(component);
|
||||||
return component;
|
return component;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||||
<uses-feature android:name="android.hardware.touchscreen" 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_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<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.USE_FINGERPRINT"/>
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<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.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||||
<uses-permission-sdk-23 android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission-sdk-23 android:name="android.permission.USE_BIOMETRIC" />
|
||||||
|
|
||||||
@@ -113,6 +113,15 @@
|
|||||||
/>
|
/>
|
||||||
</activity>
|
</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
|
<activity
|
||||||
android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity"
|
android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity"
|
||||||
android:label="@string/groups_create_group_title"
|
android:label="@string/groups_create_group_title"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.android;
|
|||||||
|
|
||||||
import android.arch.lifecycle.ViewModelProvider;
|
import android.arch.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.BrambleAndroidEagerSingletons;
|
||||||
import org.briarproject.bramble.BrambleAndroidModule;
|
import org.briarproject.bramble.BrambleAndroidModule;
|
||||||
import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
||||||
import org.briarproject.bramble.BrambleCoreModule;
|
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.bramble.plugin.tor.CircumventionProvider;
|
||||||
import org.briarproject.briar.BriarCoreEagerSingletons;
|
import org.briarproject.briar.BriarCoreEagerSingletons;
|
||||||
import org.briarproject.briar.BriarCoreModule;
|
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.login.SignInReminderReceiver;
|
||||||
import org.briarproject.briar.android.reporting.BriarReportSender;
|
import org.briarproject.briar.android.reporting.BriarReportSender;
|
||||||
import org.briarproject.briar.android.view.TextInputView;
|
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.BlogPostFactory;
|
||||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||||
import org.briarproject.briar.api.client.MessageTracker;
|
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.feed.FeedManager;
|
||||||
import org.briarproject.briar.api.forum.ForumManager;
|
import org.briarproject.briar.api.forum.ForumManager;
|
||||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||||
import org.briarproject.briar.api.introduction.IntroductionManager;
|
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.MessagingManager;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
||||||
import org.briarproject.briar.api.privategroup.GroupMessageFactory;
|
import org.briarproject.briar.api.privategroup.GroupMessageFactory;
|
||||||
@@ -68,7 +70,8 @@ import dagger.Component;
|
|||||||
AppModule.class
|
AppModule.class
|
||||||
})
|
})
|
||||||
public interface AndroidComponent
|
public interface AndroidComponent
|
||||||
extends BrambleCoreEagerSingletons, BriarCoreEagerSingletons {
|
extends BrambleCoreEagerSingletons, BrambleAndroidEagerSingletons,
|
||||||
|
BriarCoreEagerSingletons {
|
||||||
|
|
||||||
// Exposed objects
|
// Exposed objects
|
||||||
@CryptoExecutor
|
@CryptoExecutor
|
||||||
@@ -168,6 +171,8 @@ public interface AndroidComponent
|
|||||||
|
|
||||||
void inject(TextInputView textInputView);
|
void inject(TextInputView textInputView);
|
||||||
|
|
||||||
|
void inject(BriarModelLoader briarModelLoader);
|
||||||
|
|
||||||
// Eager singleton load
|
// Eager singleton load
|
||||||
void inject(AppModule.EagerSingletons init);
|
void inject(AppModule.EagerSingletons init);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,10 +66,14 @@ public class AppModule {
|
|||||||
@Inject
|
@Inject
|
||||||
AndroidNotificationManager androidNotificationManager;
|
AndroidNotificationManager androidNotificationManager;
|
||||||
@Inject
|
@Inject
|
||||||
|
ScreenFilterMonitor screenFilterMonitor;
|
||||||
|
@Inject
|
||||||
NetworkUsageLogger networkUsageLogger;
|
NetworkUsageLogger networkUsageLogger;
|
||||||
@Inject
|
@Inject
|
||||||
DozeWatchdog dozeWatchdog;
|
DozeWatchdog dozeWatchdog;
|
||||||
@Inject
|
@Inject
|
||||||
|
LockManager lockManager;
|
||||||
|
@Inject
|
||||||
RecentEmoji recentEmoji;
|
RecentEmoji recentEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import com.vanniktech.emoji.google.GoogleEmojiProvider;
|
|||||||
import org.acra.ACRA;
|
import org.acra.ACRA;
|
||||||
import org.acra.ReportingInteractionMode;
|
import org.acra.ReportingInteractionMode;
|
||||||
import org.acra.annotation.ReportsCrashes;
|
import org.acra.annotation.ReportsCrashes;
|
||||||
|
import org.briarproject.bramble.BrambleAndroidModule;
|
||||||
import org.briarproject.bramble.BrambleCoreModule;
|
import org.briarproject.bramble.BrambleCoreModule;
|
||||||
import org.briarproject.briar.BriarCoreModule;
|
import org.briarproject.briar.BriarCoreModule;
|
||||||
import org.briarproject.briar.R;
|
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
|
// We need to load the eager singletons directly after making the
|
||||||
// dependency graphs
|
// dependency graphs
|
||||||
BrambleCoreModule.initEagerSingletons(androidComponent);
|
BrambleCoreModule.initEagerSingletons(androidComponent);
|
||||||
|
BrambleAndroidModule.initEagerSingletons(androidComponent);
|
||||||
BriarCoreModule.initEagerSingletons(androidComponent);
|
BriarCoreModule.initEagerSingletons(androidComponent);
|
||||||
AndroidEagerSingletons.initEagerSingletons(androidComponent);
|
AndroidEagerSingletons.initEagerSingletons(androidComponent);
|
||||||
return androidComponent;
|
return androidComponent;
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ import org.briarproject.briar.android.blog.ReblogFragment;
|
|||||||
import org.briarproject.briar.android.blog.RssFeedImportActivity;
|
import org.briarproject.briar.android.blog.RssFeedImportActivity;
|
||||||
import org.briarproject.briar.android.blog.RssFeedManageActivity;
|
import org.briarproject.briar.android.blog.RssFeedManageActivity;
|
||||||
import org.briarproject.briar.android.blog.WriteBlogPostActivity;
|
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.ContactListFragment;
|
||||||
import org.briarproject.briar.android.contact.ContactModule;
|
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.ConversationActivity;
|
||||||
|
import org.briarproject.briar.android.conversation.ImageActivity;
|
||||||
import org.briarproject.briar.android.forum.CreateForumActivity;
|
import org.briarproject.briar.android.forum.CreateForumActivity;
|
||||||
import org.briarproject.briar.android.forum.ForumActivity;
|
import org.briarproject.briar.android.forum.ForumActivity;
|
||||||
import org.briarproject.briar.android.forum.ForumListFragment;
|
import org.briarproject.briar.android.forum.ForumListFragment;
|
||||||
@@ -110,6 +111,8 @@ public interface ActivityComponent {
|
|||||||
|
|
||||||
void inject(ConversationActivity activity);
|
void inject(ConversationActivity activity);
|
||||||
|
|
||||||
|
void inject(ImageActivity activity);
|
||||||
|
|
||||||
void inject(ForumInvitationActivity activity);
|
void inject(ForumInvitationActivity activity);
|
||||||
|
|
||||||
void inject(BlogInvitationActivity activity);
|
void inject(BlogInvitationActivity activity);
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ package org.briarproject.briar.android.activity;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.transition.Slide;
|
|
||||||
import android.transition.Transition;
|
import android.transition.Transition;
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.widget.CheckBox;
|
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_DOZE_WHITELISTING;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
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.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.getDozeWhitelistingIntent;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.isSamsung7;
|
import static org.briarproject.briar.android.util.UiUtils.isSamsung7;
|
||||||
|
|
||||||
@@ -111,21 +111,28 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
lockManager.onActivityStop();
|
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
|
// workaround for #1007
|
||||||
if (isSamsung7()) {
|
if (isSamsung7()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Transition slide = new Slide(Gravity.RIGHT);
|
if (enterTransition != null) excludeSystemUi(enterTransition);
|
||||||
slide.excludeTarget(android.R.id.statusBarBackground, true);
|
if (exitTransition != null) excludeSystemUi(exitTransition);
|
||||||
slide.excludeTarget(android.R.id.navigationBarBackground, true);
|
if (returnTransition != null) excludeSystemUi(returnTransition);
|
||||||
Window window = getWindow();
|
Window window = getWindow();
|
||||||
window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
|
window.setEnterTransition(enterTransition);
|
||||||
window.setExitTransition(slide);
|
window.setExitTransition(exitTransition);
|
||||||
window.setEnterTransition(slide);
|
window.setReturnTransition(returnTransition);
|
||||||
window.setTransitionBackgroundFadeDuration(getResources()
|
|
||||||
.getInteger(android.R.integer.config_longAnimTime));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ public class ReblogActivity extends BriarActivity implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
setSceneTransitionAnimation();
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,11 +9,15 @@ import android.os.Bundle;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.design.widget.Snackbar;
|
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.v4.content.ContextCompat;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.widget.ActionMenuView;
|
import android.support.v7.widget.ActionMenuView;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.transition.Slide;
|
||||||
|
import android.transition.Transition;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -24,6 +28,7 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
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.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
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.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.blog.BlogActivity;
|
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.conversation.ConversationVisitor.TextCache;
|
||||||
import org.briarproject.briar.android.forum.ForumActivity;
|
import org.briarproject.briar.android.forum.ForumActivity;
|
||||||
import org.briarproject.briar.android.introduction.IntroductionActivity;
|
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.conversation.event.ConversationMessageReceivedEvent;
|
||||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||||
import org.briarproject.briar.api.introduction.IntroductionManager;
|
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.MessagingManager;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessage;
|
import org.briarproject.briar.api.messaging.PrivateMessage;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
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;
|
||||||
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.PromptStateChangeListener;
|
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.v4.view.ViewCompat.setTransitionName;
|
||||||
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
||||||
|
import static android.view.Gravity.END;
|
||||||
import static android.widget.Toast.LENGTH_SHORT;
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.Collections.sort;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
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.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION;
|
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.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
|
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
|
||||||
@@ -115,7 +130,7 @@ import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.S
|
|||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class ConversationActivity extends BriarActivity
|
public class ConversationActivity extends BriarActivity
|
||||||
implements EventListener, ConversationListener, TextInputListener,
|
implements EventListener, ConversationListener, TextInputListener,
|
||||||
TextCache {
|
TextCache, AttachmentCache {
|
||||||
|
|
||||||
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
||||||
|
|
||||||
@@ -133,6 +148,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
Executor cryptoExecutor;
|
Executor cryptoExecutor;
|
||||||
|
|
||||||
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
|
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
|
||||||
|
private AttachmentController attachmentController;
|
||||||
|
|
||||||
private ConversationViewModel viewModel;
|
private ConversationViewModel viewModel;
|
||||||
private ConversationVisitor visitor;
|
private ConversationVisitor visitor;
|
||||||
@@ -142,6 +158,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
private ImageView toolbarStatus;
|
private ImageView toolbarStatus;
|
||||||
private TextView toolbarTitle;
|
private TextView toolbarTitle;
|
||||||
private BriarRecyclerView list;
|
private BriarRecyclerView list;
|
||||||
|
private LinearLayoutManager layoutManager;
|
||||||
private TextInputView textInputView;
|
private TextInputView textInputView;
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
// Fields that are accessed from background threads must be volatile
|
||||||
@@ -179,7 +196,10 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle state) {
|
public void onCreate(@Nullable Bundle state) {
|
||||||
setSceneTransitionAnimation();
|
if (SDK_INT >= 21) {
|
||||||
|
Transition slide = new Slide(END);
|
||||||
|
setSceneTransitionAnimation(slide, null, slide);
|
||||||
|
}
|
||||||
super.onCreate(state);
|
super.onCreate(state);
|
||||||
|
|
||||||
Intent i = getIntent();
|
Intent i = getIntent();
|
||||||
@@ -190,6 +210,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
viewModel = ViewModelProviders.of(this, viewModelFactory)
|
viewModel = ViewModelProviders.of(this, viewModelFactory)
|
||||||
.get(ConversationViewModel.class);
|
.get(ConversationViewModel.class);
|
||||||
viewModel.setContactId(contactId);
|
viewModel.setContactId(contactId);
|
||||||
|
attachmentController = viewModel.getAttachmentController();
|
||||||
|
|
||||||
setContentView(R.layout.activity_conversation);
|
setContentView(R.layout.activity_conversation);
|
||||||
|
|
||||||
@@ -216,11 +237,12 @@ public class ConversationActivity extends BriarActivity
|
|||||||
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
|
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
|
||||||
setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
|
setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
|
||||||
|
|
||||||
visitor = new ConversationVisitor(this, this,
|
visitor = new ConversationVisitor(this, this, this,
|
||||||
viewModel.getContactDisplayName());
|
viewModel.getContactDisplayName());
|
||||||
adapter = new ConversationAdapter(this, this);
|
adapter = new ConversationAdapter(this, this);
|
||||||
list = findViewById(R.id.conversationView);
|
list = findViewById(R.id.conversationView);
|
||||||
list.setLayoutManager(new LinearLayoutManager(this));
|
layoutManager = new LinearLayoutManager(this);
|
||||||
|
list.setLayoutManager(layoutManager);
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
list.setEmptyText(getString(R.string.no_private_messages));
|
list.setEmptyText(getString(R.string.no_private_messages));
|
||||||
|
|
||||||
@@ -330,7 +352,43 @@ public class ConversationActivity extends BriarActivity
|
|||||||
Collection<ConversationMessageHeader> headers =
|
Collection<ConversationMessageHeader> headers =
|
||||||
conversationManager.getMessageHeaders(contactId);
|
conversationManager.getMessageHeaders(contactId);
|
||||||
logDuration(LOG, "Loading messages", start);
|
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) {
|
} catch (NoSuchContactException e) {
|
||||||
finishOnUiThread();
|
finishOnUiThread();
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
@@ -387,16 +445,44 @@ public class ConversationActivity extends BriarActivity
|
|||||||
private void displayMessageText(MessageId m, String text) {
|
private void displayMessageText(MessageId m, String text) {
|
||||||
runOnUiThreadUnlessDestroyed(() -> {
|
runOnUiThreadUnlessDestroyed(() -> {
|
||||||
textCache.put(m, text);
|
textCache.put(m, text);
|
||||||
SparseArray<ConversationMessageItem> messages =
|
Pair<Integer, ConversationMessageItem> pair =
|
||||||
adapter.getMessageItems();
|
adapter.getMessageItem(m);
|
||||||
for (int i = 0; i < messages.size(); i++) {
|
if (pair != null) {
|
||||||
ConversationItem item = messages.valueAt(i);
|
pair.getSecond().setText(text);
|
||||||
if (item.getId().equals(m)) {
|
boolean bottom = adapter.isScrolledToBottom(layoutManager);
|
||||||
item.setText(text);
|
adapter.notifyItemChanged(pair.getFirst());
|
||||||
adapter.notifyItemChanged(messages.keyAt(i));
|
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
|
||||||
list.scrollToPosition(adapter.getItemCount() - 1);
|
}
|
||||||
return;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
private void addConversationItem(ConversationItem item) {
|
||||||
runOnUiThreadUnlessDestroyed(() -> {
|
runOnUiThreadUnlessDestroyed(() -> {
|
||||||
|
boolean bottom = adapter.isScrolledToBottom(layoutManager);
|
||||||
adapter.incrementRevision();
|
adapter.incrementRevision();
|
||||||
adapter.add(item);
|
adapter.add(item);
|
||||||
// Scroll to the bottom
|
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
|
||||||
list.scrollToPosition(adapter.getItemCount() - 1);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -729,6 +815,31 @@ public class ConversationActivity extends BriarActivity
|
|||||||
startActivity(i);
|
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
|
@DatabaseExecutor
|
||||||
private void respondToIntroductionRequest(SessionId sessionId,
|
private void respondToIntroductionRequest(SessionId sessionId,
|
||||||
boolean accept, long time) throws DbException {
|
boolean accept, long time) throws DbException {
|
||||||
@@ -761,4 +872,16 @@ public class ConversationActivity extends BriarActivity
|
|||||||
if (text == null) loadMessageText(m);
|
if (text == null) loadMessageText(m);
|
||||||
return text;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ package org.briarproject.briar.android.conversation;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.LayoutRes;
|
import android.support.annotation.LayoutRes;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.util.BriarAdapter;
|
import org.briarproject.briar.android.util.BriarAdapter;
|
||||||
|
|
||||||
@@ -98,16 +101,20 @@ class ConversationAdapter
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
SparseArray<ConversationMessageItem> getMessageItems() {
|
@Nullable
|
||||||
SparseArray<ConversationMessageItem> messages = new SparseArray<>();
|
Pair<Integer, ConversationMessageItem> getMessageItem(MessageId messageId) {
|
||||||
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
for (int i = 0; i < items.size(); i++) {
|
||||||
ConversationItem item = items.get(i);
|
ConversationItem item = items.get(i);
|
||||||
if (item instanceof ConversationMessageItem) {
|
if (item instanceof ConversationMessageItem &&
|
||||||
messages.put(i, (ConversationMessageItem) item);
|
item.getId().equals(messageId)) {
|
||||||
|
return new Pair<>(i, (ConversationMessageItem) item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return messages;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isScrolledToBottom(LinearLayoutManager layoutManager) {
|
||||||
|
return layoutManager.findLastVisibleItemPosition() == items.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package org.briarproject.briar.android.conversation;
|
|||||||
import android.support.annotation.CallSuper;
|
import android.support.annotation.CallSuper;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
import android.support.constraint.ConstraintLayout;
|
||||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
@@ -18,11 +18,11 @@ import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
abstract class ConversationItemViewHolder extends ViewHolder {
|
abstract class ConversationItemViewHolder extends ViewHolder {
|
||||||
|
|
||||||
protected final ViewGroup layout;
|
protected final ConstraintLayout layout;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final OutItemViewHolder outViewHolder;
|
private final OutItemViewHolder outViewHolder;
|
||||||
private final TextView text;
|
private final TextView text;
|
||||||
private final TextView time;
|
protected final TextView time;
|
||||||
|
|
||||||
ConversationItemViewHolder(View v, boolean isIncoming) {
|
ConversationItemViewHolder(View v, boolean isIncoming) {
|
||||||
super(v);
|
super(v);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.conversation;
|
||||||
|
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
@@ -14,4 +15,7 @@ interface ConversationListener {
|
|||||||
|
|
||||||
void openRequestedShareable(ConversationRequestItem item);
|
void openRequestedShareable(ConversationRequestItem item);
|
||||||
|
|
||||||
|
void onAttachmentClicked(View view, ConversationMessageItem messageItem,
|
||||||
|
AttachmentItem attachmentItem);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.briar.android.conversation;
|
|||||||
import android.support.annotation.LayoutRes;
|
import android.support.annotation.LayoutRes;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -14,15 +13,20 @@ import javax.annotation.concurrent.NotThreadSafe;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ConversationMessageItem extends ConversationItem {
|
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);
|
super(layoutRes, h);
|
||||||
this.attachments = h.getAttachmentHeaders();
|
this.attachments = attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AttachmentHeader> getAttachments() {
|
List<AttachmentItem> getAttachments() {
|
||||||
return attachments;
|
return attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAttachments(List<AttachmentItem> attachments) {
|
||||||
|
this.attachments = attachments;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,161 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
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.annotation.UiThread;
|
||||||
|
import android.support.constraint.ConstraintSet;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.view.View;
|
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.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
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ConversationMessageViewHolder extends ConversationItemViewHolder {
|
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) {
|
ConversationMessageViewHolder(View v, boolean isIncoming) {
|
||||||
super(v, 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
|
|||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -38,6 +39,7 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final ContactManager contactManager;
|
private final ContactManager contactManager;
|
||||||
|
private final AttachmentController attachmentController;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private ContactId contactId = null;
|
private ContactId contactId = null;
|
||||||
@@ -52,10 +54,12 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
@Inject
|
@Inject
|
||||||
ConversationViewModel(Application application,
|
ConversationViewModel(Application application,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
ContactManager contactManager) {
|
ContactManager contactManager, MessagingManager messagingManager) {
|
||||||
super(application);
|
super(application);
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.contactManager = contactManager;
|
this.contactManager = contactManager;
|
||||||
|
this.attachmentController = new AttachmentController(messagingManager,
|
||||||
|
application.getResources());
|
||||||
contactDeleted.setValue(false);
|
contactDeleted.setValue(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +100,10 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AttachmentController getAttachmentController() {
|
||||||
|
return attachmentController;
|
||||||
|
}
|
||||||
|
|
||||||
LiveData<Contact> getContact() {
|
LiveData<Contact> getContact() {
|
||||||
return contact;
|
return contact;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,17 +9,21 @@ import org.briarproject.bramble.api.sync.MessageId;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.api.blog.BlogInvitationRequest;
|
import org.briarproject.briar.api.blog.BlogInvitationRequest;
|
||||||
import org.briarproject.briar.api.blog.BlogInvitationResponse;
|
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.ForumInvitationRequest;
|
||||||
import org.briarproject.briar.api.forum.ForumInvitationResponse;
|
import org.briarproject.briar.api.forum.ForumInvitationResponse;
|
||||||
import org.briarproject.briar.api.introduction.IntroductionRequest;
|
import org.briarproject.briar.api.introduction.IntroductionRequest;
|
||||||
import org.briarproject.briar.api.introduction.IntroductionResponse;
|
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.messaging.PrivateMessageHeader;
|
||||||
import org.briarproject.briar.api.conversation.ConversationMessageVisitor;
|
|
||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest;
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest;
|
||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse;
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
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.BLOG;
|
||||||
import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.FORUM;
|
import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.FORUM;
|
||||||
import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.GROUP;
|
import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.GROUP;
|
||||||
@@ -33,24 +37,33 @@ class ConversationVisitor implements
|
|||||||
|
|
||||||
private final Context ctx;
|
private final Context ctx;
|
||||||
private final TextCache textCache;
|
private final TextCache textCache;
|
||||||
|
private final AttachmentCache attachmentCache;
|
||||||
private final LiveData<String> contactName;
|
private final LiveData<String> contactName;
|
||||||
|
|
||||||
ConversationVisitor(Context ctx, TextCache textCache,
|
ConversationVisitor(Context ctx, TextCache textCache,
|
||||||
LiveData<String> contactName) {
|
AttachmentCache attachmentCache, LiveData<String> contactName) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.textCache = textCache;
|
this.textCache = textCache;
|
||||||
|
this.attachmentCache = attachmentCache;
|
||||||
this.contactName = contactName;
|
this.contactName = contactName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConversationItem visitPrivateMessageHeader(PrivateMessageHeader h) {
|
public ConversationItem visitPrivateMessageHeader(PrivateMessageHeader h) {
|
||||||
ConversationItem item;
|
ConversationItem item;
|
||||||
|
List<AttachmentItem> attachments;
|
||||||
|
if (h.getAttachmentHeaders().isEmpty()) {
|
||||||
|
attachments = emptyList();
|
||||||
|
} else {
|
||||||
|
attachments = attachmentCache
|
||||||
|
.getAttachmentItems(h.getId(), h.getAttachmentHeaders());
|
||||||
|
}
|
||||||
if (h.isLocal()) {
|
if (h.isLocal()) {
|
||||||
item = new ConversationMessageItem(
|
item = new ConversationMessageItem(
|
||||||
R.layout.list_item_conversation_msg_out, h);
|
R.layout.list_item_conversation_msg_out, h, attachments);
|
||||||
} else {
|
} else {
|
||||||
item = new ConversationMessageItem(
|
item = new ConversationMessageItem(
|
||||||
R.layout.list_item_conversation_msg_in, h);
|
R.layout.list_item_conversation_msg_in, h, attachments);
|
||||||
}
|
}
|
||||||
if (h.hasText()) {
|
if (h.hasText()) {
|
||||||
String text = textCache.getText(h.getId());
|
String text = textCache.getText(h.getId());
|
||||||
@@ -216,11 +229,18 @@ class ConversationVisitor implements
|
|||||||
return new ConversationNoticeItem(
|
return new ConversationNoticeItem(
|
||||||
R.layout.list_item_conversation_notice_out, text, r);
|
R.layout.list_item_conversation_notice_out, text, r);
|
||||||
} else {
|
} else {
|
||||||
String text = ctx.getString(R.string.introduction_request_received,
|
String text;
|
||||||
contactName.getValue(), name);
|
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(
|
return new ConversationRequestItem(
|
||||||
R.layout.list_item_conversation_request, text, INTRODUCTION,
|
R.layout.list_item_conversation_request, text,
|
||||||
r);
|
INTRODUCTION, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,4 +292,9 @@ class ConversationVisitor implements
|
|||||||
@Nullable
|
@Nullable
|
||||||
String getText(MessageId m);
|
String getText(MessageId m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AttachmentCache {
|
||||||
|
List<AttachmentItem> getAttachmentItems(MessageId m,
|
||||||
|
List<AttachmentHeader> headers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.briarproject.briar.android.conversation.glide;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.GlideBuilder;
|
||||||
|
import com.bumptech.glide.Registry;
|
||||||
|
import com.bumptech.glide.annotation.GlideModule;
|
||||||
|
import com.bumptech.glide.module.AppGlideModule;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
|
import org.briarproject.briar.android.conversation.AttachmentItem;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static android.util.Log.DEBUG;
|
||||||
|
import static android.util.Log.WARN;
|
||||||
|
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||||
|
|
||||||
|
@GlideModule
|
||||||
|
@NotNullByDefault
|
||||||
|
public final class BriarGlideModule extends AppGlideModule {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerComponents(Context context, Glide glide,
|
||||||
|
Registry registry) {
|
||||||
|
BriarApplication app =
|
||||||
|
(BriarApplication) context.getApplicationContext();
|
||||||
|
BriarModelLoaderFactory factory = new BriarModelLoaderFactory(app);
|
||||||
|
registry.prepend(AttachmentItem.class, InputStream.class, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyOptions(Context context, GlideBuilder builder) {
|
||||||
|
builder.setLogLevel(IS_DEBUG_BUILD ? DEBUG : WARN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isManifestParsingEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.briarproject.briar.android.conversation.glide;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.MultiTransformation;
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||||
|
|
||||||
|
public class BriarImageTransformation extends MultiTransformation<Bitmap> {
|
||||||
|
|
||||||
|
public BriarImageTransformation(int smallRadius, int radius,
|
||||||
|
boolean leftCornerSmall, boolean bottomRound) {
|
||||||
|
super(new CenterCrop(), new ImageCornerTransformation(
|
||||||
|
smallRadius, radius, leftCornerSmall, bottomRound));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.briarproject.briar.android.conversation.glide;
|
||||||
|
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.Options;
|
||||||
|
import com.bumptech.glide.load.model.ModelLoader;
|
||||||
|
import com.bumptech.glide.signature.ObjectKey;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
|
import org.briarproject.briar.android.conversation.AttachmentItem;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public final class BriarModelLoader
|
||||||
|
implements ModelLoader<AttachmentItem, InputStream> {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
BriarDataFetcherFactory dataFetcherFactory;
|
||||||
|
|
||||||
|
public BriarModelLoader(BriarApplication app) {
|
||||||
|
app.getApplicationComponent().inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoadData<InputStream> buildLoadData(AttachmentItem model, int width,
|
||||||
|
int height, Options options) {
|
||||||
|
ObjectKey key = new ObjectKey(model.getMessageId());
|
||||||
|
BriarDataFetcher dataFetcher =
|
||||||
|
dataFetcherFactory.createBriarDataFetcher(model);
|
||||||
|
return new LoadData<>(key, dataFetcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handles(AttachmentItem model) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package org.briarproject.briar.android.conversation.glide;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.model.ModelLoader;
|
||||||
|
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
||||||
|
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
|
import org.briarproject.briar.android.conversation.AttachmentItem;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class BriarModelLoaderFactory
|
||||||
|
implements ModelLoaderFactory<AttachmentItem, InputStream> {
|
||||||
|
|
||||||
|
private final BriarApplication app;
|
||||||
|
|
||||||
|
public BriarModelLoaderFactory(BriarApplication app) {
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelLoader<AttachmentItem, InputStream> build(
|
||||||
|
MultiModelLoaderFactory multiFactory) {
|
||||||
|
return new BriarModelLoader(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void teardown() {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package org.briarproject.briar.android.conversation.glide;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapShader;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static android.graphics.Bitmap.Config.ARGB_8888;
|
||||||
|
import static android.graphics.Shader.TileMode.CLAMP;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class ImageCornerTransformation extends BitmapTransformation {
|
||||||
|
|
||||||
|
private static final String ID = ImageCornerTransformation.class.getName();
|
||||||
|
|
||||||
|
private final int smallRadius, radius;
|
||||||
|
private final boolean leftCornerSmall, bottomRound;
|
||||||
|
|
||||||
|
ImageCornerTransformation(int smallRadius, int radius,
|
||||||
|
boolean leftCornerSmall, boolean bottomRound) {
|
||||||
|
this.smallRadius = smallRadius;
|
||||||
|
this.radius = radius;
|
||||||
|
this.leftCornerSmall = leftCornerSmall;
|
||||||
|
this.bottomRound = bottomRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Bitmap transform(BitmapPool pool, Bitmap toTransform,
|
||||||
|
int outWidth, int outHeight) {
|
||||||
|
int width = toTransform.getWidth();
|
||||||
|
int height = toTransform.getHeight();
|
||||||
|
|
||||||
|
Bitmap bitmap = pool.get(width, height, ARGB_8888);
|
||||||
|
bitmap.setHasAlpha(true);
|
||||||
|
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
paint.setShader(new BitmapShader(toTransform, CLAMP, CLAMP));
|
||||||
|
drawRect(canvas, paint, width, height);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawRect(Canvas canvas, Paint paint, float width,
|
||||||
|
float height) {
|
||||||
|
drawSmallCorner(canvas, paint, width);
|
||||||
|
drawBigCorners(canvas, paint, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawSmallCorner(Canvas canvas, Paint paint, float width) {
|
||||||
|
float left = leftCornerSmall ? 0 : width - radius;
|
||||||
|
float right = leftCornerSmall ? radius : width;
|
||||||
|
canvas.drawRoundRect(new RectF(left, 0, right, radius),
|
||||||
|
smallRadius, smallRadius, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawBigCorners(Canvas canvas, Paint paint, float width,
|
||||||
|
float height) {
|
||||||
|
float top = bottomRound ? 0 : radius;
|
||||||
|
RectF rect = new RectF(0, top, width, height);
|
||||||
|
if (bottomRound) {
|
||||||
|
canvas.drawRoundRect(rect, radius, radius, paint);
|
||||||
|
} else {
|
||||||
|
canvas.drawRect(rect, paint);
|
||||||
|
canvas.drawRoundRect(new RectF(0, 0, width, radius * 2),
|
||||||
|
radius, radius, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ImageCornerTransformation(smallRadius=" + smallRadius +
|
||||||
|
", radius=" + radius + ", leftCornerSmall=" + leftCornerSmall +
|
||||||
|
", bottomRound=" + bottomRound + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof ImageCornerTransformation &&
|
||||||
|
((ImageCornerTransformation) o).smallRadius == smallRadius &&
|
||||||
|
((ImageCornerTransformation) o).radius == radius &&
|
||||||
|
((ImageCornerTransformation) o).leftCornerSmall ==
|
||||||
|
leftCornerSmall &&
|
||||||
|
((ImageCornerTransformation) o).bottomRound == bottomRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return ID.hashCode() + (smallRadius << 16) ^ (radius << 2) ^
|
||||||
|
(leftCornerSmall ? 2 : 0) ^ (bottomRound ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
||||||
|
messageDigest.update((ID + '|' + smallRadius + '|' + radius + '|' +
|
||||||
|
leftCornerSmall + '|' + bottomRound).getBytes(CHARSET));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user