mirror of
https://github.com/signalapp/libsignal.git
synced 2026-05-08 16:13:04 +02:00
jni: When exception conversion fails, make an AssertionError instead
This can fail too, but we should at least try.
This commit is contained in:
@@ -156,6 +156,8 @@ public object NativeTesting {
|
||||
@JvmStatic
|
||||
public external fun TESTING_FutureThrowsCustomErrorType(asyncRuntime: ObjectHandle): CompletableFuture<Void?>
|
||||
@JvmStatic
|
||||
public external fun TESTING_FutureThrowsPoisonErrorType(asyncRuntime: ObjectHandle): CompletableFuture<Void?>
|
||||
@JvmStatic
|
||||
public external fun TESTING_InputStreamReadIntoZeroLengthSlice(capsAlphabetInput: InputStream): ByteArray
|
||||
@JvmStatic
|
||||
public external fun TESTING_JoinStringArray(array: Array<Object>, joinWith: String): String
|
||||
|
||||
@@ -53,6 +53,20 @@ public class FutureTest {
|
||||
assertTrue(e.getCause() instanceof org.signal.libsignal.internal.TestingException);
|
||||
}
|
||||
|
||||
@Test(timeout = 5000)
|
||||
public void testFutureThrowsInvalidException() throws Exception {
|
||||
Future future = NativeTesting.TESTING_FutureThrowsPoisonErrorType(ioRuntime);
|
||||
ExecutionException e = assertThrows(ExecutionException.class, () -> future.get());
|
||||
assertTrue(e.getCause() instanceof AssertionError);
|
||||
// Check the whole message to make sure it includes both the original error and the failure to
|
||||
// convert it to an exception. (TestingError just makes that feel especially confusing!)
|
||||
assertEquals(
|
||||
e.getCause().getMessage(),
|
||||
"failed to convert error \"TestingError(org.signal.libsignal.internal.GuaranteedNonexistentException)\": "
|
||||
+ "exception in method call 'org.signal.libsignal.internal.GuaranteedNonexistentException': "
|
||||
+ "exception java.lang.NoClassDefFoundError \"org/signal/libsignal/internal/GuaranteedNonexistentException\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFutureFromRustCancel() {
|
||||
TokioAsyncContext context = new TokioAsyncContext();
|
||||
|
||||
@@ -255,6 +255,26 @@ async fn TESTING_FutureThrowsCustomErrorType() -> Result<(), CustomErrorType> {
|
||||
std::future::ready(Err(CustomErrorType)).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "jni")]
|
||||
struct PoisonErrorType;
|
||||
|
||||
#[cfg(feature = "jni")]
|
||||
impl From<PoisonErrorType> for crate::jni::SignalJniError {
|
||||
fn from(PoisonErrorType: PoisonErrorType) -> Self {
|
||||
crate::jni::TestingError {
|
||||
exception_class: crate::jni::ClassName(
|
||||
"org.signal.libsignal.internal.GuaranteedNonexistentException",
|
||||
),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[bridge_io(NonSuspendingBackgroundThreadRuntime, ffi = false, node = false)]
|
||||
async fn TESTING_FutureThrowsPoisonErrorType() -> Result<(), PoisonErrorType> {
|
||||
std::future::ready(Err(PoisonErrorType)).await
|
||||
}
|
||||
|
||||
#[bridge_fn]
|
||||
fn TESTING_ReturnStringArray() -> Box<[String]> {
|
||||
["easy", "as", "ABC", "123"]
|
||||
|
||||
@@ -111,7 +111,7 @@ impl ChatListener for JniChatListener {
|
||||
fn connection_interrupted(&mut self, disconnect_cause: DisconnectCause) {
|
||||
let listener = &self.listener;
|
||||
attach_and_log_on_error(&self.vm, "connection interrupted", move |env| {
|
||||
let throw_exception = move |env, listener, throwable: JThrowable<'_>| {
|
||||
let report_to_java = move |env, listener, throwable: JThrowable<'_>| {
|
||||
call_method_checked(
|
||||
env,
|
||||
listener,
|
||||
@@ -122,21 +122,18 @@ impl ChatListener for JniChatListener {
|
||||
};
|
||||
match disconnect_cause {
|
||||
DisconnectCause::LocalDisconnect => {
|
||||
throw_exception(env, listener, JObject::null().into())?
|
||||
report_to_java(env, listener, JObject::null().into())?
|
||||
}
|
||||
DisconnectCause::Error(disconnect_cause) => {
|
||||
let throwable = SignalJniError::from(disconnect_cause).to_throwable(env);
|
||||
throwable
|
||||
.and_then(|throwable| report_to_java(env, listener, throwable))
|
||||
.unwrap_or_else(|error| {
|
||||
log::error!(
|
||||
"failed to call onConnectionInterrupted with cause: {error}"
|
||||
);
|
||||
});
|
||||
}
|
||||
DisconnectCause::Error(disconnect_cause) => convert_to_exception(
|
||||
env,
|
||||
SignalJniError::from(disconnect_cause),
|
||||
move |env, throwable, _error| {
|
||||
throwable
|
||||
.and_then(|throwable| throw_exception(env, listener, throwable))
|
||||
.unwrap_or_else(|error| {
|
||||
log::error!(
|
||||
"failed to call onConnectionInterrupted with cause: {error}"
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
};
|
||||
Ok(())
|
||||
});
|
||||
|
||||
@@ -28,16 +28,38 @@ pub struct TestingError {
|
||||
pub(super) struct AllConnectionAttemptsFailed;
|
||||
|
||||
impl SignalJniError {
|
||||
#[cold]
|
||||
pub(super) fn to_throwable<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
self.0.to_throwable(env)
|
||||
self.0.to_throwable_impl(env).or_else(|convert_error| {
|
||||
// Recover by producing *some* throwable (AssertionError). This is particularly important
|
||||
// for Futures, which will otherwise hang. However, if this fails, give up and return the
|
||||
// *original* BridgeLayerError.
|
||||
try_scoped(|| {
|
||||
let message = env
|
||||
.new_string(format!(
|
||||
"failed to convert error \"{self}\": {convert_error}"
|
||||
))
|
||||
.check_exceptions(env, "JniError::into_throwable")?;
|
||||
let error_obj = new_instance(
|
||||
env,
|
||||
ClassName("java.lang.AssertionError"),
|
||||
jni_args!((message => java.lang.Object) -> void),
|
||||
)?;
|
||||
Ok(error_obj.into())
|
||||
})
|
||||
.map_err(|_: BridgeLayerError| convert_error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) trait JniError: Debug + Display {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError>;
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError>;
|
||||
}
|
||||
|
||||
/// Simpler trait that provides a blanket impl of [`JniError`].
|
||||
@@ -51,7 +73,10 @@ pub(super) trait MessageOnlyExceptionJniError: Debug + Display {
|
||||
}
|
||||
|
||||
impl<M: MessageOnlyExceptionJniError> JniError for M {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
let class = self.exception_class();
|
||||
let throwable = env
|
||||
.new_string(self.to_string())
|
||||
|
||||
@@ -150,30 +150,29 @@ impl<T: for<'a> ResultTypeInfo<'a> + std::panic::UnwindSafe, U> ResultReporter
|
||||
let future_for_convert = &future;
|
||||
let stack_elements_for_convert = &future_creation_stack_trace_elements;
|
||||
maybe_error.unwrap_or_else(move |error| {
|
||||
convert_to_exception(env, error, move |env, throwable, error| {
|
||||
throwable
|
||||
.and_then(move |throwable| {
|
||||
call_method_checked(
|
||||
env,
|
||||
&throwable,
|
||||
"setStackTrace",
|
||||
jni_args!((stack_elements_for_convert => [java.lang.StackTraceElement]) -> void),
|
||||
)?;
|
||||
let throwable = error.to_throwable(env);
|
||||
throwable
|
||||
.and_then(move |throwable| {
|
||||
call_method_checked(
|
||||
env,
|
||||
&throwable,
|
||||
"setStackTrace",
|
||||
jni_args!((stack_elements_for_convert => [java.lang.StackTraceElement]) -> void),
|
||||
)?;
|
||||
|
||||
_ = call_method_checked(
|
||||
env,
|
||||
future_for_convert,
|
||||
"completeExceptionally",
|
||||
jni_args!((throwable => java.lang.Throwable) -> boolean),
|
||||
)?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap_or_else(|completion_error| {
|
||||
log::error!(
|
||||
"failed to complete Future with error \"{error}\": {completion_error}"
|
||||
);
|
||||
});
|
||||
})
|
||||
_ = call_method_checked(
|
||||
env,
|
||||
future_for_convert,
|
||||
"completeExceptionally",
|
||||
jni_args!((throwable => java.lang.Throwable) -> boolean),
|
||||
)?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap_or_else(|completion_error| {
|
||||
log::error!(
|
||||
"failed to complete Future with error \"{error}\": {completion_error}"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Explicitly drop these while the thread is still attached to the JVM.
|
||||
|
||||
@@ -109,20 +109,11 @@ impl<'a, T> From<JavaCompletableFuture<'a, T>> for JObject<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn convert_to_exception<'a, 'env, F>(env: &'a mut JNIEnv<'env>, error: SignalJniError, consume: F)
|
||||
where
|
||||
F: 'a + FnOnce(&'a mut JNIEnv<'env>, Result<JThrowable<'a>, BridgeLayerError>, SignalJniError),
|
||||
{
|
||||
// This could be inlined, but then we'd have one copy per unique type for
|
||||
// `F`. That's expensive in terms of code size, so we break out the
|
||||
// invariant part into a separate function.
|
||||
let throwable = error.to_throwable(env);
|
||||
consume(env, throwable, error)
|
||||
}
|
||||
|
||||
impl JniError for BridgeLayerError {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
let class_name = match self {
|
||||
BridgeLayerError::CallbackException(_callback, exception) => {
|
||||
return env
|
||||
@@ -160,7 +151,10 @@ impl JniError for BridgeLayerError {
|
||||
}
|
||||
|
||||
impl JniError for IllegalArgumentError {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
make_single_message_throwable(
|
||||
env,
|
||||
&self.0,
|
||||
@@ -170,7 +164,10 @@ impl JniError for IllegalArgumentError {
|
||||
}
|
||||
|
||||
impl JniError for SignalProtocolError {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_java_string<'env>(
|
||||
env: &mut JNIEnv<'env>,
|
||||
s: impl Into<jni::strings::JNIString>,
|
||||
@@ -317,7 +314,10 @@ impl JniError for SignalProtocolError {
|
||||
}
|
||||
|
||||
impl JniError for libsignal_protocol::FingerprintError {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
let class_name = match self {
|
||||
Self::VersionMismatch { theirs, ours } => return new_instance(
|
||||
env,
|
||||
@@ -354,7 +354,10 @@ fn make_single_message_throwable<'a>(
|
||||
}
|
||||
|
||||
impl JniError for IoError {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
if self.kind() == std::io::ErrorKind::Other {
|
||||
let thrown_exception = self
|
||||
.get_ref()
|
||||
@@ -374,7 +377,10 @@ impl JniError for IoError {
|
||||
}
|
||||
|
||||
impl JniError for libsignal_message_backup::ReadError {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
let Self {
|
||||
error,
|
||||
found_unknown_fields,
|
||||
@@ -571,19 +577,19 @@ mod registration {
|
||||
use super::*;
|
||||
|
||||
impl<E: JniError> JniError for RequestError<E> {
|
||||
fn to_throwable<'a>(
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
let message = match self {
|
||||
RequestError::Other(inner) => return inner.to_throwable(env),
|
||||
RequestError::Other(inner) => return inner.to_throwable_impl(env),
|
||||
RequestError::Timeout => {
|
||||
return libsignal_net::chat::SendError::RequestTimedOut.to_throwable(env);
|
||||
return libsignal_net::chat::SendError::RequestTimedOut.to_throwable_impl(env);
|
||||
}
|
||||
RequestError::RetryLater(retry_later) => return retry_later.to_throwable(env),
|
||||
RequestError::RetryLater(retry_later) => return retry_later.to_throwable_impl(env),
|
||||
RequestError::Unexpected { log_safe } => log_safe,
|
||||
RequestError::Challenge(rate_limit_challenge) => {
|
||||
return rate_limit_challenge.to_throwable(env);
|
||||
return rate_limit_challenge.to_throwable_impl(env);
|
||||
}
|
||||
RequestError::ServerSideError => &self.to_string(),
|
||||
RequestError::Disconnected(d) => match *d {},
|
||||
@@ -624,30 +630,30 @@ mod registration {
|
||||
}
|
||||
|
||||
impl JniError for CreateSessionError {
|
||||
fn to_throwable<'a>(
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
match self {
|
||||
CreateSessionError::InvalidSessionId => InvalidSessionId.to_throwable(env),
|
||||
CreateSessionError::InvalidSessionId => InvalidSessionId.to_throwable_impl(env),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JniError for ResumeSessionError {
|
||||
fn to_throwable<'a>(
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
match self {
|
||||
ResumeSessionError::InvalidSessionId => InvalidSessionId.to_throwable(env),
|
||||
ResumeSessionError::InvalidSessionId => InvalidSessionId.to_throwable_impl(env),
|
||||
ResumeSessionError::SessionNotFound => session_not_found(env, &self.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JniError for UpdateSessionError {
|
||||
fn to_throwable<'a>(
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
@@ -662,13 +668,13 @@ mod registration {
|
||||
}
|
||||
|
||||
impl JniError for RequestVerificationCodeError {
|
||||
fn to_throwable<'a>(
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
match self {
|
||||
RequestVerificationCodeError::InvalidSessionId => {
|
||||
InvalidSessionId.to_throwable(env)
|
||||
InvalidSessionId.to_throwable_impl(env)
|
||||
}
|
||||
RequestVerificationCodeError::SessionNotFound => {
|
||||
session_not_found(env, &self.to_string())
|
||||
@@ -706,12 +712,14 @@ mod registration {
|
||||
}
|
||||
|
||||
impl JniError for SubmitVerificationError {
|
||||
fn to_throwable<'a>(
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
match self {
|
||||
SubmitVerificationError::InvalidSessionId => InvalidSessionId.to_throwable(env),
|
||||
SubmitVerificationError::InvalidSessionId => {
|
||||
InvalidSessionId.to_throwable_impl(env)
|
||||
}
|
||||
SubmitVerificationError::SessionNotFound => {
|
||||
session_not_found(env, &self.to_string())
|
||||
}
|
||||
@@ -733,7 +741,7 @@ mod registration {
|
||||
}
|
||||
|
||||
impl JniError for RegisterAccountError {
|
||||
fn to_throwable<'a>(
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
@@ -777,10 +785,13 @@ mod registration {
|
||||
}
|
||||
|
||||
impl JniError for CdsiError {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
let class = match *self {
|
||||
CdsiError::RateLimited(retry_later) => {
|
||||
return retry_later.to_throwable(env);
|
||||
return retry_later.to_throwable_impl(env);
|
||||
}
|
||||
CdsiError::InvalidToken => {
|
||||
ClassName("org.signal.libsignal.net.CdsiInvalidTokenException")
|
||||
@@ -817,9 +828,12 @@ impl MessageOnlyExceptionJniError for InvalidUri {
|
||||
}
|
||||
|
||||
impl JniError for ChatConnectError {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
let class = match *self {
|
||||
ChatConnectError::RetryLater(retry_later) => return retry_later.to_throwable(env),
|
||||
ChatConnectError::RetryLater(retry_later) => return retry_later.to_throwable_impl(env),
|
||||
ChatConnectError::AppExpired => {
|
||||
ClassName("org.signal.libsignal.net.AppExpiredException")
|
||||
}
|
||||
@@ -862,7 +876,10 @@ impl MessageOnlyExceptionJniError for ChatSendError {
|
||||
}
|
||||
|
||||
impl JniError for SvrbError {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
match self {
|
||||
SvrbError::RestoreFailed(tries_remaining) => {
|
||||
let message = env
|
||||
@@ -883,7 +900,7 @@ impl JniError for SvrbError {
|
||||
&self.to_string(),
|
||||
ClassName("org.signal.libsignal.svr.DataMissingException"),
|
||||
),
|
||||
SvrbError::AttestationError(inner) => inner.to_throwable(env),
|
||||
SvrbError::AttestationError(inner) => inner.to_throwable_impl(env),
|
||||
SvrbError::Protocol(_) => make_single_message_throwable(
|
||||
env,
|
||||
&self.to_string(),
|
||||
@@ -896,7 +913,7 @@ impl JniError for SvrbError {
|
||||
&self.to_string(),
|
||||
ClassName("org.signal.libsignal.net.NetworkException"),
|
||||
),
|
||||
SvrbError::RateLimited(inner) => inner.to_throwable(env),
|
||||
SvrbError::RateLimited(inner) => inner.to_throwable_impl(env),
|
||||
SvrbError::PreviousBackupDataInvalid
|
||||
| SvrbError::MetadataInvalid
|
||||
| SvrbError::DecryptionError(_) => make_single_message_throwable(
|
||||
@@ -941,13 +958,19 @@ impl MessageOnlyExceptionJniError for TestingError {
|
||||
}
|
||||
|
||||
impl JniError for Infallible {
|
||||
fn to_throwable<'a>(&self, _env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
_env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl JniError for RetryLater {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
let Self {
|
||||
retry_after_seconds,
|
||||
} = self;
|
||||
@@ -961,7 +984,10 @@ impl JniError for RetryLater {
|
||||
}
|
||||
|
||||
impl JniError for RateLimitChallenge {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
let Self { token, options } = self;
|
||||
let (message, token) =
|
||||
try_scoped(|| Ok((env.new_string(self.to_string())?, env.new_string(token)?)))
|
||||
@@ -980,16 +1006,19 @@ impl JniError for RateLimitChallenge {
|
||||
}
|
||||
|
||||
impl<E: JniError> JniError for ChatRequestError<E> {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
match self {
|
||||
ChatRequestError::Timeout => make_single_message_throwable(
|
||||
env,
|
||||
"Request timed out",
|
||||
ClassName("org.signal.libsignal.net.TimeoutException"),
|
||||
),
|
||||
ChatRequestError::Disconnected(disconnected) => disconnected.to_throwable(env),
|
||||
ChatRequestError::RetryLater(retry_later) => retry_later.to_throwable(env),
|
||||
ChatRequestError::Challenge(challenge) => challenge.to_throwable(env),
|
||||
ChatRequestError::Disconnected(disconnected) => disconnected.to_throwable_impl(env),
|
||||
ChatRequestError::RetryLater(retry_later) => retry_later.to_throwable_impl(env),
|
||||
ChatRequestError::Challenge(challenge) => challenge.to_throwable_impl(env),
|
||||
ChatRequestError::ServerSideError => make_single_message_throwable(
|
||||
env,
|
||||
"Server-side error",
|
||||
@@ -1000,13 +1029,16 @@ impl<E: JniError> JniError for ChatRequestError<E> {
|
||||
&format!("Unexpected error: {}", log_safe),
|
||||
ClassName("org.signal.libsignal.net.UnexpectedResponseException"),
|
||||
),
|
||||
ChatRequestError::Other(inner) => inner.to_throwable(env),
|
||||
ChatRequestError::Other(inner) => inner.to_throwable_impl(env),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JniError for libsignal_net_chat::api::messages::MultiRecipientSendFailure {
|
||||
fn to_throwable<'a>(&self, env: &mut JNIEnv<'a>) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
fn to_throwable_impl<'a>(
|
||||
&self,
|
||||
env: &mut JNIEnv<'a>,
|
||||
) -> Result<JThrowable<'a>, BridgeLayerError> {
|
||||
let message = self.to_string();
|
||||
match self {
|
||||
Self::Unauthorized => make_single_message_throwable(
|
||||
@@ -1037,7 +1069,7 @@ impl JniError for libsignal_net_chat::api::messages::MultiRecipientSendFailure {
|
||||
/// appropriate Java exception class and thrown.
|
||||
#[cold]
|
||||
fn throw_error(env: &mut JNIEnv, error: SignalJniError) {
|
||||
convert_to_exception(env, error, |env, throwable, error| match throwable {
|
||||
match error.to_throwable(env) {
|
||||
Err(failure) => log::error!("failed to create exception for {error}: {failure}"),
|
||||
Ok(throwable) => {
|
||||
let result = env.throw(throwable);
|
||||
@@ -1045,7 +1077,7 @@ fn throw_error(env: &mut JNIEnv, error: SignalJniError) {
|
||||
log::error!("failed to throw exception for {error}: {failure}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
Reference in New Issue
Block a user