mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 11:19:04 +01:00
Compare commits
9 Commits
beta-1.5.1
...
remove-dep
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6eaa54783 | ||
|
|
070a0181d9 | ||
|
|
d83ae3a3b4 | ||
|
|
143f04bf1b | ||
|
|
138fa6f39d | ||
|
|
8e1371acf0 | ||
|
|
29f0b9d3c0 | ||
|
|
eb45ccfe9e | ||
|
|
e98b5a9882 |
@@ -54,38 +54,6 @@ public interface CryptoComponent {
|
|||||||
KeyPair ourKeyPair, byte[]... inputs)
|
KeyPair ourKeyPair, byte[]... inputs)
|
||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a shared secret from two static and two ephemeral key pairs.
|
|
||||||
* <p>
|
|
||||||
* Do not use this method for new protocols. The shared secret can be
|
|
||||||
* re-derived using the ephemeral public keys and both static private
|
|
||||||
* keys, so keys derived from the shared secret should not be used if
|
|
||||||
* forward secrecy is required. Use {@link #deriveSharedSecret(String,
|
|
||||||
* PublicKey, PublicKey, KeyPair, KeyPair, boolean, byte[]...)} instead.
|
|
||||||
* <p>
|
|
||||||
* TODO: Remove this after a reasonable migration period (added 2023-03-10).
|
|
||||||
* <p>
|
|
||||||
*
|
|
||||||
* @param label A namespaced label indicating the purpose of this shared
|
|
||||||
* secret, to prevent it from being repurposed or colliding with a shared
|
|
||||||
* secret derived for another purpose
|
|
||||||
* @param theirStaticPublicKey The static public key of the remote party
|
|
||||||
* @param theirEphemeralPublicKey The ephemeral public key of the remote
|
|
||||||
* party
|
|
||||||
* @param ourStaticKeyPair The static key pair of the local party
|
|
||||||
* @param ourEphemeralKeyPair The ephemeral key pair of the local party
|
|
||||||
* @param alice True if the local party is Alice
|
|
||||||
* @param inputs Additional inputs that will be included in the
|
|
||||||
* derivation of the shared secret
|
|
||||||
* @return The shared secret
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
SecretKey deriveSharedSecretBadly(String label,
|
|
||||||
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
|
||||||
KeyPair ourStaticKeyPair, KeyPair ourEphemeralKeyPair,
|
|
||||||
boolean alice, byte[]... inputs)
|
|
||||||
throws GeneralSecurityException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a shared secret from two static and two ephemeral key pairs.
|
* Derives a shared secret from two static and two ephemeral key pairs.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -14,16 +14,6 @@ interface HandshakeConstants {
|
|||||||
*/
|
*/
|
||||||
byte PROTOCOL_MINOR_VERSION = 1;
|
byte PROTOCOL_MINOR_VERSION = 1;
|
||||||
|
|
||||||
/**
|
|
||||||
* Label for deriving the master key when using the deprecated v0.0 key
|
|
||||||
* derivation method.
|
|
||||||
* <p>
|
|
||||||
* TODO: Remove this after a reasonable migration period (added 2023-03-10).
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
String MASTER_KEY_LABEL_0_0 =
|
|
||||||
"org.briarproject.bramble.handshake/MASTER_KEY";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label for deriving the master key when using the v0.1 key derivation
|
* Label for deriving the master key when using the v0.1 key derivation
|
||||||
* method.
|
* method.
|
||||||
|
|||||||
@@ -12,20 +12,6 @@ interface HandshakeCrypto {
|
|||||||
|
|
||||||
KeyPair generateEphemeralKeyPair();
|
KeyPair generateEphemeralKeyPair();
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives the master key from the given static and ephemeral keys using
|
|
||||||
* the deprecated v0.0 key derivation method.
|
|
||||||
* <p>
|
|
||||||
* TODO: Remove this after a reasonable migration period (added 2023-03-10).
|
|
||||||
*
|
|
||||||
* @param alice Whether the local peer is Alice
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
SecretKey deriveMasterKey_0_0(PublicKey theirStaticPublicKey,
|
|
||||||
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
|
||||||
KeyPair ourEphemeralKeyPair, boolean alice)
|
|
||||||
throws GeneralSecurityException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives the master key from the given static and ephemeral keys using
|
* Derives the master key from the given static and ephemeral keys using
|
||||||
* the v0.1 key derivation method.
|
* the v0.1 key derivation method.
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static org.briarproject.bramble.contact.HandshakeConstants.ALICE_PROOF_LABEL;
|
import static org.briarproject.bramble.contact.HandshakeConstants.ALICE_PROOF_LABEL;
|
||||||
import static org.briarproject.bramble.contact.HandshakeConstants.BOB_PROOF_LABEL;
|
import static org.briarproject.bramble.contact.HandshakeConstants.BOB_PROOF_LABEL;
|
||||||
import static org.briarproject.bramble.contact.HandshakeConstants.MASTER_KEY_LABEL_0_0;
|
|
||||||
import static org.briarproject.bramble.contact.HandshakeConstants.MASTER_KEY_LABEL_0_1;
|
import static org.briarproject.bramble.contact.HandshakeConstants.MASTER_KEY_LABEL_0_1;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -32,27 +31,6 @@ class HandshakeCryptoImpl implements HandshakeCrypto {
|
|||||||
return crypto.generateAgreementKeyPair();
|
return crypto.generateAgreementKeyPair();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Deprecated
|
|
||||||
public SecretKey deriveMasterKey_0_0(PublicKey theirStaticPublicKey,
|
|
||||||
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
|
||||||
KeyPair ourEphemeralKeyPair, boolean alice) throws
|
|
||||||
GeneralSecurityException {
|
|
||||||
byte[] theirStatic = theirStaticPublicKey.getEncoded();
|
|
||||||
byte[] theirEphemeral = theirEphemeralPublicKey.getEncoded();
|
|
||||||
byte[] ourStatic = ourStaticKeyPair.getPublic().getEncoded();
|
|
||||||
byte[] ourEphemeral = ourEphemeralKeyPair.getPublic().getEncoded();
|
|
||||||
byte[][] inputs = {
|
|
||||||
alice ? ourStatic : theirStatic,
|
|
||||||
alice ? theirStatic : ourStatic,
|
|
||||||
alice ? ourEphemeral : theirEphemeral,
|
|
||||||
alice ? theirEphemeral : ourEphemeral
|
|
||||||
};
|
|
||||||
return crypto.deriveSharedSecretBadly(MASTER_KEY_LABEL_0_0,
|
|
||||||
theirStaticPublicKey, theirEphemeralPublicKey,
|
|
||||||
ourStaticKeyPair, ourEphemeralKeyPair, alice, inputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey deriveMasterKey_0_1(PublicKey theirStaticPublicKey,
|
public SecretKey deriveMasterKey_0_1(PublicKey theirStaticPublicKey,
|
||||||
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.contact;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.Pair;
|
import org.briarproject.bramble.api.Pair;
|
||||||
|
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
import org.briarproject.bramble.api.contact.HandshakeManager;
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
@@ -111,21 +112,12 @@ class HandshakeManagerImpl implements HandshakeManager {
|
|||||||
sendMinorVersion(recordWriter);
|
sendMinorVersion(recordWriter);
|
||||||
sendPublicKey(recordWriter, ourEphemeralKeyPair.getPublic());
|
sendPublicKey(recordWriter, ourEphemeralKeyPair.getPublic());
|
||||||
}
|
}
|
||||||
byte theirMinorVersion = theirMinorVersionAndKey.getFirst();
|
|
||||||
PublicKey theirEphemeralPublicKey = theirMinorVersionAndKey.getSecond();
|
PublicKey theirEphemeralPublicKey = theirMinorVersionAndKey.getSecond();
|
||||||
SecretKey masterKey;
|
SecretKey masterKey;
|
||||||
try {
|
try {
|
||||||
if (theirMinorVersion > 0) {
|
masterKey = handshakeCrypto.deriveMasterKey_0_1(
|
||||||
masterKey = handshakeCrypto.deriveMasterKey_0_1(
|
theirStaticPublicKey, theirEphemeralPublicKey,
|
||||||
theirStaticPublicKey, theirEphemeralPublicKey,
|
ourStaticKeyPair, ourEphemeralKeyPair, alice);
|
||||||
ourStaticKeyPair, ourEphemeralKeyPair, alice);
|
|
||||||
} else {
|
|
||||||
// TODO: Remove this branch after a reasonable migration
|
|
||||||
// period (added 2023-03-10).
|
|
||||||
masterKey = handshakeCrypto.deriveMasterKey_0_0(
|
|
||||||
theirStaticPublicKey, theirEphemeralPublicKey,
|
|
||||||
ourStaticKeyPair, ourEphemeralKeyPair, alice);
|
|
||||||
}
|
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
@@ -187,10 +179,11 @@ class HandshakeManagerImpl implements HandshakeManager {
|
|||||||
} else {
|
} else {
|
||||||
// The remote peer did not send a minor version record, so the
|
// The remote peer did not send a minor version record, so the
|
||||||
// remote peer's protocol minor version is assumed to be zero
|
// remote peer's protocol minor version is assumed to be zero
|
||||||
// TODO: Remove this branch after a reasonable migration period
|
|
||||||
// (added 2023-03-10).
|
// TODO: How communicate to user that contact seems to use a version
|
||||||
theirMinorVersion = 0;
|
// of Briar that is too old? (be aware of MITM attacks)
|
||||||
theirEphemeralPublicKey = parsePublicKey(first);
|
// `RendezvousPollerImpl` broadcasts PendingContactState FAILED via EventBus
|
||||||
|
throw new UnsupportedVersionException(true);
|
||||||
}
|
}
|
||||||
return new Pair<>(theirMinorVersion, theirEphemeralPublicKey);
|
return new Pair<>(theirMinorVersion, theirEphemeralPublicKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,36 +222,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return new SecretKey(hash);
|
return new SecretKey(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Deprecated
|
|
||||||
public SecretKey deriveSharedSecretBadly(String label,
|
|
||||||
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
|
||||||
KeyPair ourStaticKeyPair, KeyPair ourEphemeralKeyPair,
|
|
||||||
boolean alice, byte[]... inputs) throws GeneralSecurityException {
|
|
||||||
PrivateKey ourStaticPrivateKey = ourStaticKeyPair.getPrivate();
|
|
||||||
PrivateKey ourEphemeralPrivateKey = ourEphemeralKeyPair.getPrivate();
|
|
||||||
byte[][] hashInputs = new byte[inputs.length + 3][];
|
|
||||||
// Alice static/Bob static
|
|
||||||
hashInputs[0] = performRawKeyAgreement(ourStaticPrivateKey,
|
|
||||||
theirStaticPublicKey);
|
|
||||||
// Alice static/Bob ephemeral, Bob static/Alice ephemeral
|
|
||||||
if (alice) {
|
|
||||||
hashInputs[1] = performRawKeyAgreement(ourStaticPrivateKey,
|
|
||||||
theirEphemeralPublicKey);
|
|
||||||
hashInputs[2] = performRawKeyAgreement(ourEphemeralPrivateKey,
|
|
||||||
theirStaticPublicKey);
|
|
||||||
} else {
|
|
||||||
hashInputs[1] = performRawKeyAgreement(ourEphemeralPrivateKey,
|
|
||||||
theirStaticPublicKey);
|
|
||||||
hashInputs[2] = performRawKeyAgreement(ourStaticPrivateKey,
|
|
||||||
theirEphemeralPublicKey);
|
|
||||||
}
|
|
||||||
arraycopy(inputs, 0, hashInputs, 3, inputs.length);
|
|
||||||
byte[] hash = hash(label, hashInputs);
|
|
||||||
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException();
|
|
||||||
return new SecretKey(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey deriveSharedSecret(String label,
|
public SecretKey deriveSharedSecret(String label,
|
||||||
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
||||||
|
|||||||
@@ -89,11 +89,17 @@ class H2Database extends JdbcDatabase {
|
|||||||
try {
|
try {
|
||||||
c = createConnection();
|
c = createConnection();
|
||||||
closeAllConnections();
|
closeAllConnections();
|
||||||
setDirty(c, false);
|
LOG.info("Compacting DB");
|
||||||
s = c.createStatement();
|
s = c.createStatement();
|
||||||
s.execute("SHUTDOWN COMPACT");
|
s.execute("SHUTDOWN COMPACT");
|
||||||
|
LOG.info("Finished compacting DB");
|
||||||
s.close();
|
s.close();
|
||||||
c.close();
|
c.close();
|
||||||
|
// Reopen the DB to mark it as clean after compacting
|
||||||
|
c = createConnection();
|
||||||
|
setDirty(c, false);
|
||||||
|
LOG.info("Marked DB as clean");
|
||||||
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s, LOG, WARNING);
|
tryToClose(s, LOG, WARNING);
|
||||||
tryToClose(c, LOG, WARNING);
|
tryToClose(c, LOG, WARNING);
|
||||||
@@ -126,6 +132,7 @@ class H2Database extends JdbcDatabase {
|
|||||||
closeAllConnections();
|
closeAllConnections();
|
||||||
s = c.createStatement();
|
s = c.createStatement();
|
||||||
s.execute("SHUTDOWN COMPACT");
|
s.execute("SHUTDOWN COMPACT");
|
||||||
|
LOG.info("Finished compacting DB");
|
||||||
s.close();
|
s.close();
|
||||||
c.close();
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.contact;
|
package org.briarproject.bramble.contact;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
|
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
@@ -27,6 +28,7 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -123,12 +125,12 @@ public class HandshakeManagerImplTest extends BrambleMockTestCase {
|
|||||||
assertEquals(alice, result.isAlice());
|
assertEquals(alice, result.isAlice());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = UnsupportedVersionException.class)
|
||||||
public void testHandshakeAsAliceWithPeerVersion_0_0() throws Exception {
|
public void testHandshakeAsAliceWithPeerVersion_0_0() throws Exception {
|
||||||
testHandshakeWithPeerVersion_0_0(true);
|
testHandshakeWithPeerVersion_0_0(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = UnsupportedVersionException.class)
|
||||||
public void testHandshakeAsBobWithPeerVersion_0_0() throws Exception {
|
public void testHandshakeAsBobWithPeerVersion_0_0() throws Exception {
|
||||||
testHandshakeWithPeerVersion_0_0(false);
|
testHandshakeWithPeerVersion_0_0(false);
|
||||||
}
|
}
|
||||||
@@ -140,20 +142,8 @@ public class HandshakeManagerImplTest extends BrambleMockTestCase {
|
|||||||
expectSendKey();
|
expectSendKey();
|
||||||
// Remote peer does not send minor version, so use old key derivation
|
// Remote peer does not send minor version, so use old key derivation
|
||||||
expectReceiveKey();
|
expectReceiveKey();
|
||||||
expectDeriveMasterKey_0_0(alice);
|
|
||||||
expectDeriveProof(alice);
|
|
||||||
expectSendProof();
|
|
||||||
expectReceiveProof();
|
|
||||||
expectSendEof();
|
|
||||||
expectReceiveEof();
|
|
||||||
expectVerifyOwnership(alice, true);
|
|
||||||
|
|
||||||
HandshakeResult result = handshakeManager.handshake(
|
handshakeManager.handshake(pendingContact.getId(), in, streamWriter);
|
||||||
pendingContact.getId(), in, streamWriter);
|
|
||||||
|
|
||||||
assertArrayEquals(masterKey.getBytes(),
|
|
||||||
result.getMasterKey().getBytes());
|
|
||||||
assertEquals(alice, result.isAlice());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
@@ -241,15 +231,6 @@ public class HandshakeManagerImplTest extends BrambleMockTestCase {
|
|||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expectDeriveMasterKey_0_0(boolean alice) throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(handshakeCrypto).deriveMasterKey_0_0(theirStaticPublicKey,
|
|
||||||
theirEphemeralPublicKey, ourStaticKeyPair,
|
|
||||||
ourEphemeralKeyPair, alice);
|
|
||||||
will(returnValue(masterKey));
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void expectDeriveProof(boolean alice) {
|
private void expectDeriveProof(boolean alice) {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(handshakeCrypto).proveOwnership(masterKey, alice);
|
oneOf(handshakeCrypto).proveOwnership(masterKey, alice);
|
||||||
|
|||||||
@@ -60,22 +60,6 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDerivesStaticEphemeralSharedSecretBadly() throws Exception {
|
|
||||||
String label = getRandomString(123);
|
|
||||||
KeyPair aStatic = crypto.generateAgreementKeyPair();
|
|
||||||
KeyPair aEphemeral = crypto.generateAgreementKeyPair();
|
|
||||||
KeyPair bStatic = crypto.generateAgreementKeyPair();
|
|
||||||
KeyPair bEphemeral = crypto.generateAgreementKeyPair();
|
|
||||||
SecretKey aShared = crypto.deriveSharedSecretBadly(label,
|
|
||||||
bStatic.getPublic(), bEphemeral.getPublic(), aStatic,
|
|
||||||
aEphemeral, true, inputs);
|
|
||||||
SecretKey bShared = crypto.deriveSharedSecretBadly(label,
|
|
||||||
aStatic.getPublic(), aEphemeral.getPublic(), bStatic,
|
|
||||||
bEphemeral, false, inputs);
|
|
||||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDerivesStaticEphemeralSharedSecret() throws Exception {
|
public void testDerivesStaticEphemeralSharedSecret() throws Exception {
|
||||||
String label = getRandomString(123);
|
String label = getRandomString(123);
|
||||||
|
|||||||
@@ -217,19 +217,14 @@ public class BriarService extends Service {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
// Hold a wake lock during shutdown
|
super.onDestroy();
|
||||||
wakeLockManager.runWakefully(() -> {
|
LOG.info("Destroyed");
|
||||||
super.onDestroy();
|
// Stop the lifecycle, if not already stopped
|
||||||
LOG.info("Destroyed");
|
shutdown(false);
|
||||||
stopForeground(true);
|
stopForeground(true);
|
||||||
if (receiver != null) {
|
if (receiver != null) {
|
||||||
getApplicationContext().unregisterReceiver(receiver);
|
getApplicationContext().unregisterReceiver(receiver);
|
||||||
}
|
}
|
||||||
// Stop the services in a background thread
|
|
||||||
wakeLockManager.executeWakefully(() -> {
|
|
||||||
if (started) lifecycleManager.stopServices();
|
|
||||||
}, "LifecycleShutdown");
|
|
||||||
}, "LifecycleShutdown");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -299,8 +294,8 @@ public class BriarService extends Service {
|
|||||||
private void shutdownFromBackground() {
|
private void shutdownFromBackground() {
|
||||||
// Hold a wake lock during shutdown
|
// Hold a wake lock during shutdown
|
||||||
wakeLockManager.runWakefully(() -> {
|
wakeLockManager.runWakefully(() -> {
|
||||||
// Stop the service
|
// Begin lifecycle shutdown
|
||||||
stopSelf();
|
shutdown(true);
|
||||||
// Hide the UI
|
// Hide the UI
|
||||||
hideUi();
|
hideUi();
|
||||||
// Wait for shutdown to complete, then exit
|
// Wait for shutdown to complete, then exit
|
||||||
@@ -335,8 +330,18 @@ public class BriarService extends Service {
|
|||||||
/**
|
/**
|
||||||
* Starts the shutdown process.
|
* Starts the shutdown process.
|
||||||
*/
|
*/
|
||||||
public void shutdown() {
|
public void shutdown(boolean stopAndroidService) {
|
||||||
stopSelf(); // This will call onDestroy()
|
// Hold a wake lock during shutdown
|
||||||
|
wakeLockManager.runWakefully(() -> {
|
||||||
|
// Stop the lifecycle services in a background thread,
|
||||||
|
// then stop this Android service if needed
|
||||||
|
wakeLockManager.executeWakefully(() -> {
|
||||||
|
if (started) lifecycleManager.stopServices();
|
||||||
|
if (stopAndroidService) {
|
||||||
|
androidExecutor.runOnUiThread(() -> stopSelf());
|
||||||
|
}
|
||||||
|
}, "LifecycleShutdown");
|
||||||
|
}, "LifecycleShutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BriarBinder extends Binder {
|
public class BriarBinder extends Binder {
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ public class BriarControllerImpl implements BriarController {
|
|||||||
service.waitForStartup();
|
service.waitForStartup();
|
||||||
// Shut down the service and wait for it to shut down
|
// Shut down the service and wait for it to shut down
|
||||||
LOG.info("Shutting down service");
|
LOG.info("Shutting down service");
|
||||||
service.shutdown();
|
service.shutdown(true);
|
||||||
service.waitForShutdown();
|
service.waitForShutdown();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
LOG.warning("Interrupted while waiting for service");
|
LOG.warning("Interrupted while waiting for service");
|
||||||
|
|||||||
@@ -13,15 +13,18 @@ import androidx.recyclerview.widget.ListAdapter;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ForumListAdapter extends ListAdapter<ForumListItem, ForumViewHolder> {
|
class ForumListAdapter extends ListAdapter<ForumListItem, ForumViewHolder> {
|
||||||
|
|
||||||
ForumListAdapter() {
|
private final ForumListViewModel viewModel;
|
||||||
|
|
||||||
|
ForumListAdapter(ForumListViewModel viewModel) {
|
||||||
super(new ForumListCallback());
|
super(new ForumListCallback());
|
||||||
|
this.viewModel = viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ForumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public ForumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
View v = LayoutInflater.from(parent.getContext()).inflate(
|
View v = LayoutInflater.from(parent.getContext()).inflate(
|
||||||
R.layout.list_item_forum, parent, false);
|
R.layout.list_item_forum, parent, false);
|
||||||
return new ForumViewHolder(v);
|
return new ForumViewHolder(v, viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class ForumListFragment extends BaseFragment implements
|
|||||||
private ForumListViewModel viewModel;
|
private ForumListViewModel viewModel;
|
||||||
private BriarRecyclerView list;
|
private BriarRecyclerView list;
|
||||||
private Snackbar snackbar;
|
private Snackbar snackbar;
|
||||||
private final ForumListAdapter adapter = new ForumListAdapter();
|
private ForumListAdapter adapter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
@@ -54,6 +54,7 @@ public class ForumListFragment extends BaseFragment implements
|
|||||||
component.inject(this);
|
component.inject(this);
|
||||||
viewModel = new ViewModelProvider(this, viewModelFactory)
|
viewModel = new ViewModelProvider(this, viewModelFactory)
|
||||||
.get(ForumListViewModel.class);
|
.get(ForumListViewModel.class);
|
||||||
|
adapter = new ForumListAdapter(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.briar.android.forum;
|
package org.briarproject.briar.android.forum;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
@@ -15,6 +16,7 @@ import org.briarproject.bramble.api.sync.GroupId;
|
|||||||
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.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveResult;
|
import org.briarproject.briar.android.viewmodel.LiveResult;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
@@ -40,6 +42,7 @@ import androidx.annotation.UiThread;
|
|||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
@@ -180,4 +183,17 @@ class ForumListViewModel extends DbViewModel implements EventListener {
|
|||||||
return numInvitations;
|
return numInvitations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deleteForum(GroupId groupId) {
|
||||||
|
runOnDbThread(() -> {
|
||||||
|
try {
|
||||||
|
Forum f = forumManager.getForum(groupId);
|
||||||
|
forumManager.removeForum(f);
|
||||||
|
androidExecutor.runOnUiThread(() -> Toast
|
||||||
|
.makeText(getApplication(), R.string.forum_left_toast,
|
||||||
|
LENGTH_SHORT).show());
|
||||||
|
} catch (DbException e) {
|
||||||
|
handleException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
@@ -20,6 +21,7 @@ import static org.briarproject.briar.android.activity.BriarActivity.GROUP_NAME;
|
|||||||
|
|
||||||
class ForumViewHolder extends RecyclerView.ViewHolder {
|
class ForumViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private final ForumListViewModel viewModel;
|
||||||
private final Context ctx;
|
private final Context ctx;
|
||||||
private final ViewGroup layout;
|
private final ViewGroup layout;
|
||||||
private final TextAvatarView avatar;
|
private final TextAvatarView avatar;
|
||||||
@@ -27,8 +29,9 @@ class ForumViewHolder extends RecyclerView.ViewHolder {
|
|||||||
private final TextView postCount;
|
private final TextView postCount;
|
||||||
private final TextView date;
|
private final TextView date;
|
||||||
|
|
||||||
ForumViewHolder(View v) {
|
ForumViewHolder(View v, ForumListViewModel viewModel) {
|
||||||
super(v);
|
super(v);
|
||||||
|
this.viewModel = viewModel;
|
||||||
ctx = v.getContext();
|
ctx = v.getContext();
|
||||||
layout = (ViewGroup) v;
|
layout = (ViewGroup) v;
|
||||||
avatar = v.findViewById(R.id.avatarView);
|
avatar = v.findViewById(R.id.avatarView);
|
||||||
@@ -64,6 +67,21 @@ class ForumViewHolder extends RecyclerView.ViewHolder {
|
|||||||
date.setVisibility(VISIBLE);
|
date.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open popup menu on long click
|
||||||
|
layout.setOnLongClickListener(v -> {
|
||||||
|
PopupMenu pm = new PopupMenu(ctx, v);
|
||||||
|
pm.getMenuInflater().inflate(R.menu.forum_list_item_actions,
|
||||||
|
pm.getMenu());
|
||||||
|
pm.setOnMenuItemClickListener(it -> {
|
||||||
|
if (it.getItemId() == R.id.action_forum_delete) {
|
||||||
|
viewModel.deleteForum(item.getForum().getId());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
pm.show();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
// Open Forum on Click
|
// Open Forum on Click
|
||||||
layout.setOnClickListener(v -> {
|
layout.setOnClickListener(v -> {
|
||||||
Intent i = new Intent(ctx, ForumActivity.class);
|
Intent i = new Intent(ctx, ForumActivity.class);
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
android:layout_width="@dimen/listitem_picture_frame_size"
|
android:layout_width="@dimen/listitem_picture_frame_size"
|
||||||
android:layout_height="@dimen/listitem_picture_frame_size"
|
android:layout_height="@dimen/listitem_picture_frame_size"
|
||||||
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/divider"
|
app:layout_constraintBottom_toTopOf="@+id/divider"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
@@ -38,7 +37,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/margin_medium"
|
android:layout_marginTop="@dimen/margin_medium"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:textSize="@dimen/text_size_small"
|
android:textSize="@dimen/text_size_small"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/dateView"
|
app:layout_constraintEnd_toStartOf="@+id/dateView"
|
||||||
@@ -51,7 +49,6 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:textSize="@dimen/text_size_small"
|
android:textSize="@dimen/text_size_small"
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/postCountView"
|
app:layout_constraintBaseline_toBaselineOf="@+id/postCountView"
|
||||||
@@ -63,7 +60,6 @@
|
|||||||
style="@style/Divider.ThreadItem"
|
style="@style/Divider.ThreadItem"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_marginStart="@dimen/margin_medium"
|
android:layout_marginStart="@dimen/margin_medium"
|
||||||
android:layout_marginLeft="@dimen/margin_medium"
|
|
||||||
android:layout_marginTop="@dimen/listitem_horizontal_margin"
|
android:layout_marginTop="@dimen/listitem_horizontal_margin"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_forum_delete"
|
||||||
|
android:title="@string/forum_leave" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
@@ -6,9 +6,9 @@ sourceSets.configureEach { sourceSet ->
|
|||||||
|
|
||||||
idea {
|
idea {
|
||||||
module {
|
module {
|
||||||
sourceDirs += compileJava.options.generatedSourceOutputDirectory
|
sourceDirs += compileJava.options.generatedSourceOutputDirectory.get().getAsFile()
|
||||||
generatedSourceDirs += compileJava.options.generatedSourceOutputDirectory
|
generatedSourceDirs += compileJava.options.generatedSourceOutputDirectory.get().getAsFile()
|
||||||
testSourceDirs += compileTestJava.options.generatedSourceOutputDirectory
|
testSourceDirs += compileTestJava.options.generatedSourceOutputDirectory.get().getAsFile()
|
||||||
generatedSourceDirs += compileTestJava.options.generatedSourceOutputDirectory
|
generatedSourceDirs += compileTestJava.options.generatedSourceOutputDirectory.get().getAsFile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user