2017-05-20 01:01:40 +00:00
package org.thoughtcrime.securesms.jobs ;
2020-06-12 17:22:46 +00:00
import android.app.Application ;
2020-06-08 23:04:55 +00:00
import android.content.Context ;
2020-02-10 23:40:22 +00:00
import android.text.TextUtils ;
2019-06-05 19:47:14 +00:00
import androidx.annotation.NonNull ;
2019-09-07 03:40:06 +00:00
import androidx.annotation.Nullable ;
2020-06-08 23:04:55 +00:00
import androidx.annotation.WorkerThread ;
2020-07-24 19:53:49 +00:00
import com.annimon.stream.Collectors ;
2020-06-08 23:04:55 +00:00
import com.annimon.stream.Stream ;
2019-09-07 03:40:06 +00:00
2020-02-10 23:40:22 +00:00
import org.signal.zkgroup.profiles.ProfileKey ;
import org.signal.zkgroup.profiles.ProfileKeyCredential ;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil ;
2017-05-20 01:01:40 +00:00
import org.thoughtcrime.securesms.database.DatabaseFactory ;
2020-03-27 18:55:44 +00:00
import org.thoughtcrime.securesms.database.GroupDatabase ;
2018-05-22 09:13:10 +00:00
import org.thoughtcrime.securesms.database.RecipientDatabase ;
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode ;
2019-07-15 15:12:26 +00:00
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies ;
2019-03-28 15:56:35 +00:00
import org.thoughtcrime.securesms.jobmanager.Data ;
import org.thoughtcrime.securesms.jobmanager.Job ;
2020-06-08 23:04:55 +00:00
import org.thoughtcrime.securesms.jobmanager.JobManager ;
2019-03-28 15:56:35 +00:00
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint ;
2020-06-12 17:22:46 +00:00
import org.thoughtcrime.securesms.keyvalue.SignalStore ;
2018-05-22 09:13:10 +00:00
import org.thoughtcrime.securesms.logging.Log ;
2019-12-20 20:12:22 +00:00
import org.thoughtcrime.securesms.profiles.ProfileName ;
2017-05-20 01:01:40 +00:00
import org.thoughtcrime.securesms.recipients.Recipient ;
2019-08-07 18:22:51 +00:00
import org.thoughtcrime.securesms.recipients.RecipientId ;
2020-09-22 20:24:13 +00:00
import org.thoughtcrime.securesms.recipients.RecipientUtil ;
2020-11-10 17:40:50 +00:00
import org.thoughtcrime.securesms.tracing.Trace ;
2020-06-08 23:04:55 +00:00
import org.thoughtcrime.securesms.transport.RetryLaterException ;
2017-05-20 01:01:40 +00:00
import org.thoughtcrime.securesms.util.Base64 ;
2017-06-23 20:57:38 +00:00
import org.thoughtcrime.securesms.util.IdentityUtil ;
2019-11-08 20:33:10 +00:00
import org.thoughtcrime.securesms.util.ProfileUtil ;
2020-06-12 17:22:46 +00:00
import org.thoughtcrime.securesms.util.SetUtil ;
2020-06-08 23:04:55 +00:00
import org.thoughtcrime.securesms.util.Stopwatch ;
2020-10-14 17:46:17 +00:00
import org.thoughtcrime.securesms.util.TextSecurePreferences ;
2017-08-15 01:11:13 +00:00
import org.thoughtcrime.securesms.util.Util ;
2020-06-08 23:04:55 +00:00
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors ;
2017-05-20 01:01:40 +00:00
import org.whispersystems.libsignal.IdentityKey ;
import org.whispersystems.libsignal.InvalidKeyException ;
2020-06-08 23:04:55 +00:00
import org.whispersystems.libsignal.util.Pair ;
2020-02-10 23:40:22 +00:00
import org.whispersystems.libsignal.util.guava.Optional ;
2019-05-01 16:00:26 +00:00
import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException ;
2017-08-15 01:11:13 +00:00
import org.whispersystems.signalservice.api.crypto.ProfileCipher ;
2020-02-10 23:40:22 +00:00
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential ;
2017-08-08 23:37:15 +00:00
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile ;
2020-06-08 23:04:55 +00:00
import org.whispersystems.signalservice.api.push.exceptions.NotFoundException ;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException ;
import org.whispersystems.signalservice.internal.util.concurrent.ListenableFuture ;
2017-05-20 01:01:40 +00:00
import java.io.IOException ;
2020-07-24 19:53:49 +00:00
import java.util.ArrayList ;
2020-06-08 23:04:55 +00:00
import java.util.Collections ;
import java.util.HashSet ;
2017-08-01 15:56:00 +00:00
import java.util.List ;
2020-06-08 23:04:55 +00:00
import java.util.Locale ;
2020-09-22 20:24:13 +00:00
import java.util.Map ;
2020-06-08 23:04:55 +00:00
import java.util.Set ;
2020-09-22 20:24:13 +00:00
import java.util.UUID ;
2020-06-08 23:04:55 +00:00
import java.util.concurrent.ExecutionException ;
import java.util.concurrent.TimeUnit ;
import java.util.concurrent.TimeoutException ;
2017-05-20 01:01:40 +00:00
2019-11-08 20:33:10 +00:00
/ * *
2020-04-09 20:56:42 +00:00
* Retrieves a users profile and sets the appropriate local fields .
2019-11-08 20:33:10 +00:00
* /
2020-11-10 17:40:50 +00:00
@Trace
2019-07-15 15:12:26 +00:00
public class RetrieveProfileJob extends BaseJob {
2018-08-09 14:15:43 +00:00
2019-03-28 15:56:35 +00:00
public static final String KEY = "RetrieveProfileJob" ;
2017-05-20 01:01:40 +00:00
private static final String TAG = RetrieveProfileJob . class . getSimpleName ( ) ;
2020-06-08 23:04:55 +00:00
private static final String KEY_RECIPIENTS = "recipients" ;
2020-07-24 19:53:49 +00:00
private final Set < RecipientId > recipientIds ;
2020-06-08 23:04:55 +00:00
/ * *
2020-07-24 19:53:49 +00:00
* Identical to { @link # enqueue ( Set ) } ) } , but run on a background thread for convenience .
2020-06-08 23:04:55 +00:00
* /
public static void enqueueAsync ( @NonNull RecipientId recipientId ) {
2020-07-24 19:53:49 +00:00
SignalExecutors . BOUNDED . execute ( ( ) - > ApplicationDependencies . getJobManager ( ) . add ( forRecipient ( recipientId ) ) ) ;
2020-06-08 23:04:55 +00:00
}
2018-08-09 14:15:43 +00:00
2020-06-26 15:24:32 +00:00
/ * *
* Submits the necessary job to refresh the profile of the requested recipient . Works for any
* RecipientId , including individuals , groups , or yourself .
*
2020-07-24 19:53:49 +00:00
* Identical to { @link # enqueue ( Set ) } ) }
2020-06-26 15:24:32 +00:00
* /
@WorkerThread
public static void enqueue ( @NonNull RecipientId recipientId ) {
ApplicationDependencies . getJobManager ( ) . add ( forRecipient ( recipientId ) ) ;
}
2020-06-08 23:04:55 +00:00
/ * *
* Submits the necessary jobs to refresh the profiles of the requested recipients . Works for any
* RecipientIds , including individuals , groups , or yourself .
* /
@WorkerThread
2020-07-24 19:53:49 +00:00
public static void enqueue ( @NonNull Set < RecipientId > recipientIds ) {
2020-07-02 15:17:44 +00:00
JobManager jobManager = ApplicationDependencies . getJobManager ( ) ;
2020-06-08 23:04:55 +00:00
2020-07-02 15:17:44 +00:00
for ( Job job : forRecipients ( recipientIds ) ) {
jobManager . add ( job ) ;
2020-06-08 23:04:55 +00:00
}
2020-04-09 20:56:42 +00:00
}
2020-06-08 23:04:55 +00:00
/ * *
* Works for any RecipientId , whether it ' s an individual , group , or yourself .
* /
@WorkerThread
public static @NonNull Job forRecipient ( @NonNull RecipientId recipientId ) {
Recipient recipient = Recipient . resolved ( recipientId ) ;
2020-10-19 20:27:49 +00:00
if ( recipient . isSelf ( ) ) {
2020-04-09 20:56:42 +00:00
return new RefreshOwnProfileJob ( ) ;
2020-06-08 23:04:55 +00:00
} else if ( recipient . isGroup ( ) ) {
Context context = ApplicationDependencies . getApplication ( ) ;
List < Recipient > recipients = DatabaseFactory . getGroupDatabase ( context ) . getGroupMembers ( recipient . requireGroupId ( ) , GroupDatabase . MemberSet . FULL_MEMBERS_EXCLUDING_SELF ) ;
2020-07-24 19:53:49 +00:00
return new RetrieveProfileJob ( Stream . of ( recipients ) . map ( Recipient : : getId ) . collect ( Collectors . toSet ( ) ) ) ;
2020-04-09 20:56:42 +00:00
} else {
2020-07-24 19:53:49 +00:00
return new RetrieveProfileJob ( Collections . singleton ( recipientId ) ) ;
2020-04-09 20:56:42 +00:00
}
}
2020-07-02 15:17:44 +00:00
/ * *
* Works for any RecipientId , whether it ' s an individual , group , or yourself .
2020-07-24 19:53:49 +00:00
*
* @return A list of length 2 or less . Two iff you are in the recipients .
2020-07-02 15:17:44 +00:00
* /
@WorkerThread
2020-07-24 19:53:49 +00:00
public static @NonNull List < Job > forRecipients ( @NonNull Set < RecipientId > recipientIds ) {
Context context = ApplicationDependencies . getApplication ( ) ;
Set < RecipientId > combined = new HashSet < > ( recipientIds . size ( ) ) ;
boolean includeSelf = false ;
2020-07-02 15:17:44 +00:00
for ( RecipientId recipientId : recipientIds ) {
Recipient recipient = Recipient . resolved ( recipientId ) ;
2020-10-19 20:27:49 +00:00
if ( recipient . isSelf ( ) ) {
2020-07-24 19:53:49 +00:00
includeSelf = true ;
2020-07-02 15:17:44 +00:00
} else if ( recipient . isGroup ( ) ) {
List < Recipient > recipients = DatabaseFactory . getGroupDatabase ( context ) . getGroupMembers ( recipient . requireGroupId ( ) , GroupDatabase . MemberSet . FULL_MEMBERS_EXCLUDING_SELF ) ;
combined . addAll ( Stream . of ( recipients ) . map ( Recipient : : getId ) . toList ( ) ) ;
} else {
combined . add ( recipientId ) ;
}
}
2020-07-24 19:53:49 +00:00
List < Job > jobs = new ArrayList < > ( 2 ) ;
if ( includeSelf ) {
jobs . add ( new RefreshOwnProfileJob ( ) ) ;
}
if ( combined . size ( ) > 0 ) {
jobs . add ( new RetrieveProfileJob ( combined ) ) ;
}
2020-07-02 15:17:44 +00:00
return jobs ;
}
2020-06-12 17:22:46 +00:00
/ * *
* Will fetch some profiles to ensure we ' re decently up - to - date if we haven ' t done so within a
* certain time period .
* /
2020-07-24 19:53:49 +00:00
public static void enqueueRoutineFetchIfNecessary ( Application application ) {
2020-10-14 17:46:17 +00:00
if ( ! SignalStore . registrationValues ( ) . isRegistrationComplete ( ) | |
! TextSecurePreferences . isPushRegistered ( application ) | |
TextSecurePreferences . getLocalUuid ( application ) = = null )
{
2020-09-24 14:46:45 +00:00
Log . i ( TAG , "Registration not complete. Skipping." ) ;
return ;
}
2020-06-12 17:22:46 +00:00
long timeSinceRefresh = System . currentTimeMillis ( ) - SignalStore . misc ( ) . getLastProfileRefreshTime ( ) ;
if ( timeSinceRefresh < TimeUnit . HOURS . toMillis ( 12 ) ) {
Log . i ( TAG , "Too soon to refresh. Did the last refresh " + timeSinceRefresh + " ms ago." ) ;
return ;
}
SignalExecutors . BOUNDED . execute ( ( ) - > {
RecipientDatabase db = DatabaseFactory . getRecipientDatabase ( application ) ;
long current = System . currentTimeMillis ( ) ;
List < RecipientId > ids = db . getRecipientsForRoutineProfileFetch ( current - TimeUnit . DAYS . toMillis ( 30 ) ,
current - TimeUnit . DAYS . toMillis ( 1 ) ,
50 ) ;
2020-09-23 13:40:43 +00:00
ids . add ( Recipient . self ( ) . getId ( ) ) ;
2020-06-12 17:22:46 +00:00
if ( ids . size ( ) > 0 ) {
Log . i ( TAG , "Optimistically refreshing " + ids . size ( ) + " eligible recipient(s)." ) ;
2020-07-24 19:53:49 +00:00
enqueue ( new HashSet < > ( ids ) ) ;
2020-06-12 17:22:46 +00:00
} else {
Log . i ( TAG , "No recipients to refresh." ) ;
}
SignalStore . misc ( ) . setLastProfileRefreshTime ( System . currentTimeMillis ( ) ) ;
} ) ;
}
2020-07-24 19:53:49 +00:00
private RetrieveProfileJob ( @NonNull Set < RecipientId > recipientIds ) {
2019-03-28 15:56:35 +00:00
this ( new Job . Parameters . Builder ( )
. addConstraint ( NetworkConstraint . KEY )
. setMaxAttempts ( 3 )
. build ( ) ,
2020-06-08 23:04:55 +00:00
recipientIds ) ;
2018-08-09 14:15:43 +00:00
}
2017-05-20 01:01:40 +00:00
2020-07-24 19:53:49 +00:00
private RetrieveProfileJob ( @NonNull Job . Parameters parameters , @NonNull Set < RecipientId > recipientIds ) {
2019-03-28 15:56:35 +00:00
super ( parameters ) ;
2020-06-08 23:04:55 +00:00
this . recipientIds = recipientIds ;
2017-05-20 01:01:40 +00:00
}
@Override
2019-03-28 15:56:35 +00:00
public @NonNull Data serialize ( ) {
2020-06-08 23:04:55 +00:00
return new Data . Builder ( )
. putStringListAsArray ( KEY_RECIPIENTS , Stream . of ( recipientIds )
. map ( RecipientId : : serialize )
. toList ( ) )
. build ( ) ;
2018-08-09 14:15:43 +00:00
}
@Override
2019-03-28 15:56:35 +00:00
public @NonNull String getFactoryKey ( ) {
return KEY ;
2018-08-09 14:15:43 +00:00
}
2017-05-20 01:01:40 +00:00
@Override
2020-06-08 23:04:55 +00:00
public void onRun ( ) throws IOException , RetryLaterException {
2020-09-22 20:24:13 +00:00
Stopwatch stopwatch = new Stopwatch ( "RetrieveProfile" ) ;
RecipientDatabase recipientDatabase = DatabaseFactory . getRecipientDatabase ( context ) ;
Set < RecipientId > retries = new HashSet < > ( ) ;
Set < RecipientId > unregistered = new HashSet < > ( ) ;
2020-06-08 23:04:55 +00:00
2020-09-22 20:24:13 +00:00
RecipientUtil . ensureUuidsAreAvailable ( context , Stream . of ( Recipient . resolvedList ( recipientIds ) )
. filter ( r - > r . getRegistered ( ) ! = RecipientDatabase . RegisteredState . NOT_REGISTERED )
. toList ( ) ) ;
List < Recipient > recipients = Recipient . resolvedList ( recipientIds ) ;
stopwatch . split ( "resolve-ensure" ) ;
2020-06-08 23:04:55 +00:00
List < Pair < Recipient , ListenableFuture < ProfileAndCredential > > > futures = Stream . of ( recipients )
. filter ( Recipient : : hasServiceIdentifier )
. map ( r - > new Pair < > ( r , ProfileUtil . retrieveProfile ( context , r , getRequestType ( r ) ) ) )
. toList ( ) ;
stopwatch . split ( "futures" ) ;
List < Pair < Recipient , ProfileAndCredential > > profiles = Stream . of ( futures )
. map ( pair - > {
Recipient recipient = pair . first ( ) ;
try {
ProfileAndCredential profile = pair . second ( ) . get ( 5 , TimeUnit . SECONDS ) ;
return new Pair < > ( recipient , profile ) ;
} catch ( InterruptedException | TimeoutException e ) {
retries . add ( recipient . getId ( ) ) ;
} catch ( ExecutionException e ) {
if ( e . getCause ( ) instanceof PushNetworkException ) {
retries . add ( recipient . getId ( ) ) ;
} else if ( e . getCause ( ) instanceof NotFoundException ) {
Log . w ( TAG , "Failed to find a profile for " + recipient . getId ( ) ) ;
2020-09-22 20:24:13 +00:00
if ( recipient . isRegistered ( ) ) {
unregistered . add ( recipient . getId ( ) ) ;
}
2020-06-08 23:04:55 +00:00
} else {
Log . w ( TAG , "Failed to retrieve profile for " + recipient . getId ( ) ) ;
}
}
return null ;
} )
. withoutNulls ( )
. toList ( ) ;
stopwatch . split ( "network" ) ;
for ( Pair < Recipient , ProfileAndCredential > profile : profiles ) {
process ( profile . first ( ) , profile . second ( ) ) ;
}
2020-06-12 17:22:46 +00:00
Set < RecipientId > success = SetUtil . difference ( recipientIds , retries ) ;
2020-09-22 20:24:13 +00:00
recipientDatabase . markProfilesFetched ( success , System . currentTimeMillis ( ) ) ;
Map < RecipientId , String > newlyRegistered = Stream . of ( profiles )
. map ( Pair : : first )
. filterNot ( Recipient : : isRegistered )
. collect ( Collectors . toMap ( Recipient : : getId ,
r - > r . getUuid ( ) . transform ( UUID : : toString ) . orNull ( ) ) ) ;
if ( unregistered . size ( ) > 0 | | newlyRegistered . size ( ) > 0 ) {
Log . i ( TAG , "Marking " + newlyRegistered . size ( ) + " users as registered and " + unregistered . size ( ) + " users as unregistered." ) ;
recipientDatabase . bulkUpdatedRegisteredStatus ( newlyRegistered , unregistered ) ;
}
2020-06-12 17:22:46 +00:00
2020-06-08 23:04:55 +00:00
stopwatch . split ( "process" ) ;
long keyCount = Stream . of ( profiles ) . map ( Pair : : first ) . map ( Recipient : : getProfileKey ) . withoutNulls ( ) . count ( ) ;
Log . d ( TAG , String . format ( Locale . US , "Started with %d recipient(s). Found %d profile(s), and had keys for %d of them. Will retry %d." , recipients . size ( ) , profiles . size ( ) , keyCount , retries . size ( ) ) ) ;
stopwatch . stop ( TAG ) ;
2019-11-22 02:20:41 +00:00
2020-06-08 23:04:55 +00:00
recipientIds . clear ( ) ;
recipientIds . addAll ( retries ) ;
2019-10-01 18:53:13 +00:00
2020-06-08 23:04:55 +00:00
if ( recipientIds . size ( ) > 0 ) {
throw new RetryLaterException ( ) ;
}
2017-05-20 01:01:40 +00:00
}
@Override
2019-05-22 16:51:56 +00:00
public boolean onShouldRetry ( @NonNull Exception e ) {
2020-06-08 23:04:55 +00:00
return e instanceof RetryLaterException ;
2017-05-20 01:01:40 +00:00
}
@Override
2020-01-03 19:10:16 +00:00
public void onFailure ( ) { }
2017-05-20 01:01:40 +00:00
2020-07-24 19:53:49 +00:00
private void process ( Recipient recipient , ProfileAndCredential profileAndCredential ) {
2020-02-10 23:40:22 +00:00
SignalServiceProfile profile = profileAndCredential . getProfile ( ) ;
ProfileKey recipientProfileKey = ProfileKeyUtil . profileKeyOrNull ( recipient . getProfileKey ( ) ) ;
2018-05-22 09:13:10 +00:00
2017-08-22 17:44:04 +00:00
setProfileName ( recipient , profile . getName ( ) ) ;
setProfileAvatar ( recipient , profile . getAvatar ( ) ) ;
2020-08-27 13:49:45 +00:00
clearUsername ( recipient ) ;
2019-09-07 03:40:06 +00:00
setProfileCapabilities ( recipient , profile . getCapabilities ( ) ) ;
2019-11-08 20:33:10 +00:00
setIdentityKey ( recipient , profile . getIdentityKey ( ) ) ;
2018-05-22 09:13:10 +00:00
setUnidentifiedAccessMode ( recipient , profile . getUnidentifiedAccess ( ) , profile . isUnrestrictedUnidentifiedAccess ( ) ) ;
2020-02-10 23:40:22 +00:00
if ( recipientProfileKey ! = null ) {
Optional < ProfileKeyCredential > profileKeyCredential = profileAndCredential . getProfileKeyCredential ( ) ;
if ( profileKeyCredential . isPresent ( ) ) {
setProfileKeyCredential ( recipient , recipientProfileKey , profileKeyCredential . get ( ) ) ;
}
}
}
private void setProfileKeyCredential ( @NonNull Recipient recipient ,
@NonNull ProfileKey recipientProfileKey ,
@NonNull ProfileKeyCredential credential )
{
RecipientDatabase recipientDatabase = DatabaseFactory . getRecipientDatabase ( context ) ;
recipientDatabase . setProfileKeyCredential ( recipient . getId ( ) , recipientProfileKey , credential ) ;
}
private static SignalServiceProfile . RequestType getRequestType ( @NonNull Recipient recipient ) {
2020-07-07 15:00:29 +00:00
return ! recipient . hasProfileKeyCredential ( )
2020-02-10 23:40:22 +00:00
? SignalServiceProfile . RequestType . PROFILE_AND_CREDENTIAL
: SignalServiceProfile . RequestType . PROFILE ;
2017-05-20 01:01:40 +00:00
}
2017-08-15 01:11:13 +00:00
private void setIdentityKey ( Recipient recipient , String identityKeyValue ) {
try {
if ( TextUtils . isEmpty ( identityKeyValue ) ) {
Log . w ( TAG , "Identity key is missing on profile!" ) ;
return ;
}
IdentityKey identityKey = new IdentityKey ( Base64 . decode ( identityKeyValue ) , 0 ) ;
if ( ! DatabaseFactory . getIdentityDatabase ( context )
2019-08-07 18:22:51 +00:00
. getIdentity ( recipient . getId ( ) )
2017-08-15 01:11:13 +00:00
. isPresent ( ) )
{
Log . w ( TAG , "Still first use..." ) ;
return ;
}
2019-09-07 03:40:06 +00:00
IdentityUtil . saveIdentity ( context , recipient . requireServiceId ( ) , identityKey ) ;
2017-08-15 01:11:13 +00:00
} catch ( InvalidKeyException | IOException e ) {
Log . w ( TAG , e ) ;
}
}
2018-05-22 09:13:10 +00:00
private void setUnidentifiedAccessMode ( Recipient recipient , String unidentifiedAccessVerifier , boolean unrestrictedUnidentifiedAccess ) {
RecipientDatabase recipientDatabase = DatabaseFactory . getRecipientDatabase ( context ) ;
2020-02-10 23:40:22 +00:00
ProfileKey profileKey = ProfileKeyUtil . profileKeyOrNull ( recipient . getProfileKey ( ) ) ;
2018-05-22 09:13:10 +00:00
2018-11-06 17:58:45 +00:00
if ( unrestrictedUnidentifiedAccess & & unidentifiedAccessVerifier ! = null ) {
2019-10-01 18:53:13 +00:00
if ( recipient . getUnidentifiedAccessMode ( ) ! = UnidentifiedAccessMode . UNRESTRICTED ) {
Log . i ( TAG , "Marking recipient UD status as unrestricted." ) ;
recipientDatabase . setUnidentifiedAccessMode ( recipient . getId ( ) , UnidentifiedAccessMode . UNRESTRICTED ) ;
}
2018-05-22 09:13:10 +00:00
} else if ( profileKey = = null | | unidentifiedAccessVerifier = = null ) {
2019-10-01 18:53:13 +00:00
if ( recipient . getUnidentifiedAccessMode ( ) ! = UnidentifiedAccessMode . DISABLED ) {
Log . i ( TAG , "Marking recipient UD status as disabled." ) ;
recipientDatabase . setUnidentifiedAccessMode ( recipient . getId ( ) , UnidentifiedAccessMode . DISABLED ) ;
}
2018-05-22 09:13:10 +00:00
} else {
ProfileCipher profileCipher = new ProfileCipher ( profileKey ) ;
boolean verifiedUnidentifiedAccess ;
try {
verifiedUnidentifiedAccess = profileCipher . verifyUnidentifiedAccess ( Base64 . decode ( unidentifiedAccessVerifier ) ) ;
} catch ( IOException e ) {
Log . w ( TAG , e ) ;
verifiedUnidentifiedAccess = false ;
}
2018-10-11 23:45:22 +00:00
UnidentifiedAccessMode mode = verifiedUnidentifiedAccess ? UnidentifiedAccessMode . ENABLED : UnidentifiedAccessMode . DISABLED ;
2019-10-01 18:53:13 +00:00
if ( recipient . getUnidentifiedAccessMode ( ) ! = mode ) {
Log . i ( TAG , "Marking recipient UD status as " + mode . name ( ) + " after verification." ) ;
recipientDatabase . setUnidentifiedAccessMode ( recipient . getId ( ) , mode ) ;
}
2018-05-22 09:13:10 +00:00
}
}
2017-08-22 17:44:04 +00:00
private void setProfileName ( Recipient recipient , String profileName ) {
2017-08-15 01:11:13 +00:00
try {
2020-02-10 23:40:22 +00:00
ProfileKey profileKey = ProfileKeyUtil . profileKeyOrNull ( recipient . getProfileKey ( ) ) ;
2017-08-22 17:44:04 +00:00
if ( profileKey = = null ) return ;
2017-08-15 01:11:13 +00:00
2020-07-16 12:34:53 +00:00
String plaintextProfileName = Util . emptyIfNull ( ProfileUtil . decryptName ( profileKey , profileName ) ) ;
2017-08-15 01:11:13 +00:00
2020-07-24 18:20:52 +00:00
ProfileName remoteProfileName = ProfileName . fromSerialized ( plaintextProfileName ) ;
ProfileName localProfileName = recipient . getProfileName ( ) ;
2020-07-15 20:15:15 +00:00
2020-07-24 18:20:52 +00:00
if ( ! remoteProfileName . equals ( localProfileName ) ) {
2019-12-05 04:55:17 +00:00
Log . i ( TAG , "Profile name updated. Writing new value." ) ;
2020-07-24 18:20:52 +00:00
DatabaseFactory . getRecipientDatabase ( context ) . setProfileName ( recipient . getId ( ) , remoteProfileName ) ;
2020-07-15 20:15:15 +00:00
2020-07-24 18:20:52 +00:00
String remoteDisplayName = remoteProfileName . toString ( ) ;
String localDisplayName = localProfileName . toString ( ) ;
if ( ! recipient . isBlocked ( ) & &
! recipient . isGroup ( ) & &
2020-10-19 20:27:49 +00:00
! recipient . isSelf ( ) & &
2020-07-24 18:20:52 +00:00
! localDisplayName . isEmpty ( ) & &
! remoteDisplayName . equals ( localDisplayName ) )
{
2020-07-17 20:58:33 +00:00
Log . i ( TAG , "Writing a profile name change event." ) ;
2020-07-24 18:20:52 +00:00
DatabaseFactory . getSmsDatabase ( context ) . insertProfileNameChangeMessages ( recipient , remoteDisplayName , localDisplayName ) ;
2020-07-17 20:58:33 +00:00
} else {
2020-07-24 18:20:52 +00:00
Log . i ( TAG , String . format ( Locale . US , "Name changed, but wasn't relevant to write an event. blocked: %s, group: %s, self: %s, firstSet: %s, displayChange: %s" ,
2020-10-19 20:27:49 +00:00
recipient . isBlocked ( ) , recipient . isGroup ( ) , recipient . isSelf ( ) , localDisplayName . isEmpty ( ) , ! remoteDisplayName . equals ( localDisplayName ) ) ) ;
2020-07-15 20:15:15 +00:00
}
2017-08-15 01:11:13 +00:00
}
2019-12-05 04:55:17 +00:00
if ( TextUtils . isEmpty ( plaintextProfileName ) ) {
Log . i ( TAG , "No profile name set." ) ;
}
2020-06-08 23:04:55 +00:00
} catch ( InvalidCiphertextException e ) {
Log . w ( TAG , "Bad profile key for " + recipient . getId ( ) ) ;
} catch ( IOException e ) {
2017-08-15 01:11:13 +00:00
Log . w ( TAG , e ) ;
}
}
2020-07-24 19:53:49 +00:00
private static void setProfileAvatar ( Recipient recipient , String profileAvatar ) {
2017-08-22 17:44:04 +00:00
if ( recipient . getProfileKey ( ) = = null ) return ;
2017-08-15 01:11:13 +00:00
2017-08-22 17:44:04 +00:00
if ( ! Util . equals ( profileAvatar , recipient . getProfileAvatar ( ) ) ) {
2019-10-15 19:47:54 +00:00
ApplicationDependencies . getJobManager ( ) . add ( new RetrieveProfileAvatarJob ( recipient , profileAvatar ) ) ;
2017-08-15 01:11:13 +00:00
}
}
2018-05-22 09:13:10 +00:00
2020-08-27 13:49:45 +00:00
private void clearUsername ( Recipient recipient ) {
DatabaseFactory . getRecipientDatabase ( context ) . setUsername ( recipient . getId ( ) , null ) ;
2019-10-29 00:16:11 +00:00
}
2019-09-07 03:40:06 +00:00
private void setProfileCapabilities ( @NonNull Recipient recipient , @Nullable SignalServiceProfile . Capabilities capabilities ) {
if ( capabilities = = null ) {
return ;
}
2020-02-14 16:00:32 +00:00
DatabaseFactory . getRecipientDatabase ( context ) . setCapabilities ( recipient . getId ( ) , capabilities ) ;
2019-09-07 03:40:06 +00:00
}
2019-03-28 15:56:35 +00:00
public static final class Factory implements Job . Factory < RetrieveProfileJob > {
@Override
public @NonNull RetrieveProfileJob create ( @NonNull Parameters parameters , @NonNull Data data ) {
2020-07-24 19:53:49 +00:00
String [ ] ids = data . getStringArray ( KEY_RECIPIENTS ) ;
Set < RecipientId > recipientIds = Stream . of ( ids ) . map ( RecipientId : : from ) . collect ( Collectors . toSet ( ) ) ;
2020-06-08 23:04:55 +00:00
return new RetrieveProfileJob ( parameters , recipientIds ) ;
2019-03-28 15:56:35 +00:00
}
}
2017-05-20 01:01:40 +00:00
}