1 package org
.asamk
.signal
.manager
.helper
;
3 import org
.asamk
.signal
.manager
.DeviceLinkInfo
;
4 import org
.asamk
.signal
.manager
.SignalDependencies
;
5 import org
.asamk
.signal
.manager
.api
.InvalidDeviceLinkException
;
6 import org
.asamk
.signal
.manager
.config
.ServiceConfig
;
7 import org
.asamk
.signal
.manager
.storage
.SignalAccount
;
8 import org
.asamk
.signal
.manager
.util
.KeyUtils
;
9 import org
.slf4j
.Logger
;
10 import org
.slf4j
.LoggerFactory
;
11 import org
.whispersystems
.libsignal
.InvalidKeyException
;
12 import org
.whispersystems
.libsignal
.util
.guava
.Optional
;
13 import org
.whispersystems
.signalservice
.api
.push
.ACI
;
14 import org
.whispersystems
.signalservice
.api
.push
.exceptions
.AuthorizationFailedException
;
15 import org
.whispersystems
.signalservice
.api
.util
.DeviceNameUtil
;
17 import java
.io
.IOException
;
18 import java
.util
.concurrent
.TimeUnit
;
20 public class AccountHelper
{
22 private final static Logger logger
= LoggerFactory
.getLogger(AccountHelper
.class);
24 private final Context context
;
25 private final SignalAccount account
;
26 private final SignalDependencies dependencies
;
28 private Callable unregisteredListener
;
30 public AccountHelper(final Context context
) {
31 this.account
= context
.getAccount();
32 this.dependencies
= context
.getDependencies();
33 this.context
= context
;
36 public void setUnregisteredListener(final Callable unregisteredListener
) {
37 this.unregisteredListener
= unregisteredListener
;
40 public void checkAccountState() throws IOException
{
41 if (account
.getLastReceiveTimestamp() == 0) {
42 logger
.info("The Signal protocol expects that incoming messages are regularly received.");
44 var diffInMilliseconds
= System
.currentTimeMillis() - account
.getLastReceiveTimestamp();
45 long days
= TimeUnit
.DAYS
.convert(diffInMilliseconds
, TimeUnit
.MILLISECONDS
);
48 "Messages have been last received {} days ago. The Signal protocol expects that incoming messages are regularly received.",
53 context
.getPreKeyHelper().refreshPreKeysIfNecessary();
54 if (account
.getAci() == null) {
55 final var aci
= ACI
.parseOrNull(dependencies
.getAccountManager().getWhoAmI().getAci());
57 context
.getAccountFileUpdater().updateAccountIdentifiers(account
.getNumber(), aci
);
59 updateAccountAttributes();
60 } catch (AuthorizationFailedException e
) {
61 account
.setRegistered(false);
66 public void setDeviceName(String deviceName
) {
67 final var privateKey
= account
.getIdentityKeyPair().getPrivateKey();
68 final var encryptedDeviceName
= DeviceNameUtil
.encryptDeviceName(deviceName
, privateKey
);
69 account
.setEncryptedDeviceName(encryptedDeviceName
);
72 public void updateAccountAttributes() throws IOException
{
73 dependencies
.getAccountManager()
74 .setAccountAttributes(account
.getEncryptedDeviceName(),
76 account
.getLocalRegistrationId(),
79 account
.getPinMasterKey() == null ?
null : account
.getPinMasterKey().deriveRegistrationLock(),
80 account
.getSelfUnidentifiedAccessKey(),
81 account
.isUnrestrictedUnidentifiedAccess(),
82 ServiceConfig
.capabilities
,
83 account
.isDiscoverableByPhoneNumber());
86 public void addDevice(DeviceLinkInfo deviceLinkInfo
) throws IOException
, InvalidDeviceLinkException
{
87 var identityKeyPair
= account
.getIdentityKeyPair();
88 var verificationCode
= dependencies
.getAccountManager().getNewDeviceVerificationCode();
91 dependencies
.getAccountManager()
92 .addDevice(deviceLinkInfo
.deviceIdentifier(),
93 deviceLinkInfo
.deviceKey(),
95 Optional
.of(account
.getProfileKey().serialize()),
97 } catch (InvalidKeyException e
) {
98 throw new InvalidDeviceLinkException("Invalid device link", e
);
100 account
.setMultiDevice(true);
103 public void removeLinkedDevices(int deviceId
) throws IOException
{
104 dependencies
.getAccountManager().removeDevice(deviceId
);
105 var devices
= dependencies
.getAccountManager().getDevices();
106 account
.setMultiDevice(devices
.size() > 1);
109 public void setRegistrationPin(String pin
) throws IOException
{
110 final var masterKey
= account
.getPinMasterKey() != null
111 ? account
.getPinMasterKey()
112 : KeyUtils
.createMasterKey();
114 context
.getPinHelper().setRegistrationLockPin(pin
, masterKey
);
116 account
.setRegistrationLockPin(pin
, masterKey
);
119 public void removeRegistrationPin() throws IOException
{
121 context
.getPinHelper().removeRegistrationLockPin();
123 account
.setRegistrationLockPin(null, null);
126 public void unregister() throws IOException
{
127 // When setting an empty GCM id, the Signal-Server also sets the fetchesMessages property to false.
128 // If this is the master device, other users can't send messages to this number anymore.
129 // If this is a linked device, other users can still send messages, but this device doesn't receive them anymore.
130 dependencies
.getAccountManager().setGcmId(Optional
.absent());
132 account
.setRegistered(false);
133 unregisteredListener
.call();
136 public void deleteAccount() throws IOException
{
138 context
.getPinHelper().removeRegistrationLockPin();
139 } catch (IOException e
) {
140 logger
.warn("Failed to remove registration lock pin");
142 account
.setRegistrationLockPin(null, null);
144 dependencies
.getAccountManager().deleteAccount();
146 account
.setRegistered(false);
147 unregisteredListener
.call();
150 public interface Callable
{