mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-08-04 20:34:14 +01:00
Use device specific API for linked devices updating capabilities.
This commit is contained in:
committed by
Michelle Tang
parent
ee11d1fd3b
commit
c4faafa9ae
@@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.net.SignalNetwork;
|
||||
import org.thoughtcrime.securesms.registration.data.RegistrationRepository;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.api.NetworkResultUtil;
|
||||
import org.whispersystems.signalservice.api.RequestResultUtil;
|
||||
import org.whispersystems.signalservice.api.account.AccountAttributes;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||
|
||||
@@ -95,12 +96,26 @@ public class RefreshAttributesJob extends BaseJob {
|
||||
return;
|
||||
}
|
||||
|
||||
SvrValues svrValues = SignalStore.svr();
|
||||
|
||||
AccountAttributes.Capabilities capabilities = AppCapabilities.getCapabilities(svrValues.hasPin() && !svrValues.hasOptedOut());
|
||||
|
||||
if (SignalStore.account().isPrimaryDevice()) {
|
||||
setPrimaryDeviceAttributes(svrValues, capabilities);
|
||||
} else {
|
||||
Log.i(TAG, "Linked device, refreshing device capabilities only. Capabilities: " + capabilities);
|
||||
RequestResultUtil.successOrThrow(SignalNetwork.account().setCapabilities(capabilities));
|
||||
}
|
||||
|
||||
hasRefreshedThisAppCycle = true;
|
||||
}
|
||||
|
||||
private void setPrimaryDeviceAttributes(@NonNull SvrValues svrValues, @NonNull AccountAttributes.Capabilities capabilities) throws IOException {
|
||||
int registrationId = SignalStore.account().getRegistrationId();
|
||||
boolean fetchesMessages = !SignalStore.account().isFcmEnabled() || SignalStore.settings().getForceWebsocketMode().isEnabled();
|
||||
byte[] unidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey());
|
||||
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
|
||||
String registrationLockV2 = null;
|
||||
SvrValues svrValues = SignalStore.svr();
|
||||
int pniRegistrationId = RegistrationRepository.getPniRegistrationId();
|
||||
String recoveryPassword = svrValues.getMasterKey().deriveRegistrationRecoveryPassword();
|
||||
|
||||
@@ -113,7 +128,6 @@ public class RefreshAttributesJob extends BaseJob {
|
||||
String deviceName = SignalStore.account().getDeviceName();
|
||||
byte[] encryptedDeviceName = (deviceName == null) ? null : DeviceNameCipher.encryptDeviceName(deviceName.getBytes(StandardCharsets.UTF_8), SignalStore.account().getAciIdentityKey());
|
||||
|
||||
AccountAttributes.Capabilities capabilities = AppCapabilities.getCapabilities(svrValues.hasPin() && !svrValues.hasOptedOut());
|
||||
Log.i(TAG, "Calling setAccountAttributes() reglockV2? " + !TextUtils.isEmpty(registrationLockV2) + ", pin? " + svrValues.hasPin() + ", restoredAEP? " + SignalStore.account().restoredAccountEntropyPool() +
|
||||
"\n Recovery password? " + !TextUtils.isEmpty(recoveryPassword) +
|
||||
"\n Phone number discoverable : " + phoneNumberDiscoverable +
|
||||
@@ -135,8 +149,6 @@ public class RefreshAttributesJob extends BaseJob {
|
||||
);
|
||||
|
||||
NetworkResultUtil.toBasicLegacy(SignalNetwork.account().setAccountAttributes(accountAttributes));
|
||||
|
||||
hasRefreshedThisAppCycle = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -72,6 +72,11 @@ class ResetSvrGuessCountJob private constructor(
|
||||
override fun getFactoryKey(): String = KEY
|
||||
|
||||
override fun run(): Result {
|
||||
if (SignalStore.account.isLinkedDevice) {
|
||||
Log.i(TAG, "Not primary device. Skipping.")
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
SvrRepository.operationLock.withLock {
|
||||
val pin = SignalStore.svr.pin
|
||||
|
||||
|
||||
@@ -52,6 +52,11 @@ class Svr3MirrorJob private constructor(parameters: Parameters, private var seri
|
||||
override fun getFactoryKey(): String = KEY
|
||||
|
||||
override fun run(): Result {
|
||||
if (SignalStore.account.isLinkedDevice) {
|
||||
Log.i(TAG, "Not primary device. Skipping.")
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
if (!Svr3Migration.shouldWriteToSvr3) {
|
||||
Log.w(TAG, "Writes to SVR3 are disabled. Skipping.")
|
||||
return Result.success()
|
||||
|
||||
+1
-1
@@ -260,7 +260,7 @@ class AppRegistrationStorageController(private val context: Context) : StorageCo
|
||||
data.registrationLockEnabled,
|
||||
data.accountEntropyPool.isNotEmpty()
|
||||
)
|
||||
} else if (data.pinOptedOut) {
|
||||
} else if (data.pinOptedOut && data.linkedDeviceData == null) {
|
||||
Log.i(TAG, "[commitRegistrationData] User opted out of creating a PIN. Applying opt-out.")
|
||||
SvrRepository.optOutOfPin(rotateAep = false)
|
||||
}
|
||||
|
||||
+21
@@ -2,11 +2,13 @@
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
@file:JvmName("RequestResultUtil")
|
||||
|
||||
package org.whispersystems.signalservice.api
|
||||
|
||||
import org.signal.core.util.concurrent.safeBlockingGet
|
||||
import org.signal.libsignal.net.RequestResult
|
||||
import org.signal.network.exceptions.NonSuccessfulResponseCodeException
|
||||
import org.signal.network.exceptions.PushNetworkException
|
||||
import org.signal.network.rest.RestStatusCodeError
|
||||
import org.signal.network.util.JsonUtil
|
||||
@@ -49,6 +51,25 @@ fun <T : Any> SignalWebSocket.fromWebSocketRequest(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps a [RequestResult] to its success value, or throws an [IOException]. Useful for callers bridging the
|
||||
* libsignal [RequestResult] surface back into legacy, exception-based code.
|
||||
*
|
||||
* All non-2xx responses become a [NonSuccessfulResponseCodeException].
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun <T : Any> RequestResult<T, RestStatusCodeError>.successOrThrow(): T {
|
||||
return when (this) {
|
||||
is RequestResult.Success -> result
|
||||
is RequestResult.RetryableNetworkError -> throw networkError
|
||||
is RequestResult.NonSuccess -> throw NonSuccessfulResponseCodeException(error.statusCode, "StatusCode: ${error.statusCode}", error.body, error.headers)
|
||||
is RequestResult.ApplicationError -> throw when (val error = cause) {
|
||||
is IOException, is RuntimeException -> error
|
||||
else -> RuntimeException(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Any> WebsocketResponse.toRequestResult(clazz: KClass<T>): RequestResult<T, RestStatusCodeError> {
|
||||
return if (status < 200 || status > 299) {
|
||||
RequestResult.NonSuccess(RestStatusCodeError(status, headers, body?.toByteArray()))
|
||||
|
||||
+13
@@ -7,9 +7,11 @@ package org.whispersystems.signalservice.api.account
|
||||
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.Base64.encodeUrlSafeWithoutPadding
|
||||
import org.signal.libsignal.net.RequestResult
|
||||
import org.signal.libsignal.usernames.BaseUsernameException
|
||||
import org.signal.libsignal.usernames.Username
|
||||
import org.signal.network.NetworkResult
|
||||
import org.signal.network.rest.RestStatusCodeError
|
||||
import org.signal.network.websocket.WebSocketRequestMessage
|
||||
import org.signal.network.websocket.delete
|
||||
import org.signal.network.websocket.get
|
||||
@@ -77,6 +79,17 @@ class AccountApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSock
|
||||
return NetworkResult.fromWebSocketRequest(authWebSocket, request)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the capabilities of the calling device.
|
||||
*
|
||||
* PUT /v1/devices/capabilities
|
||||
* - 200: Success
|
||||
*/
|
||||
fun setCapabilities(capabilities: AccountAttributes.Capabilities): RequestResult<Unit, RestStatusCodeError> {
|
||||
val request = WebSocketRequestMessage.put("/v1/devices/capabilities", capabilities)
|
||||
return authWebSocket.fromWebSocketRequest(request, Unit::class)
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /v1/accounts/registration_lock
|
||||
* - 204: Success
|
||||
|
||||
Reference in New Issue
Block a user