Search in sources :

Example 11 with RecipientDatabase

use of org.thoughtcrime.securesms.database.RecipientDatabase in project Signal-Android by WhisperSystems.

the class DirectoryHelper method refreshDirectoryFor.

@WorkerThread
public static RegisteredState refreshDirectoryFor(@NonNull Context context, @NonNull Recipient recipient, boolean notifyOfNewUsers) throws IOException {
    Stopwatch stopwatch = new Stopwatch("single");
    RecipientDatabase recipientDatabase = SignalDatabase.recipients();
    RegisteredState originalRegisteredState = recipient.resolve().getRegistered();
    RegisteredState newRegisteredState;
    if (recipient.hasServiceId() && !recipient.hasE164()) {
        boolean isRegistered = ApplicationDependencies.getSignalServiceAccountManager().isIdentifierRegistered(recipient.requireServiceId());
        stopwatch.split("aci-network");
        if (isRegistered) {
            boolean idChanged = recipientDatabase.markRegistered(recipient.getId(), recipient.requireServiceId());
            if (idChanged) {
                Log.w(TAG, "ID changed during refresh by UUID.");
            }
        } else {
            recipientDatabase.markUnregistered(recipient.getId());
        }
        stopwatch.split("aci-disk");
        stopwatch.stop(TAG);
        return isRegistered ? RegisteredState.REGISTERED : RegisteredState.NOT_REGISTERED;
    }
    if (!recipient.getE164().isPresent()) {
        Log.w(TAG, "No ACI or E164?");
        return RegisteredState.NOT_REGISTERED;
    }
    DirectoryResult result = ContactDiscoveryV2.getDirectoryResult(context, recipient.getE164().get());
    stopwatch.split("e164-network");
    if (result.getNumberRewrites().size() > 0) {
        Log.i(TAG, "[getDirectoryResult] Need to rewrite some numbers.");
        recipientDatabase.updatePhoneNumbers(result.getNumberRewrites());
    }
    if (result.getRegisteredNumbers().size() > 0) {
        ACI aci = result.getRegisteredNumbers().values().iterator().next();
        if (aci != null) {
            boolean idChanged = recipientDatabase.markRegistered(recipient.getId(), aci);
            if (idChanged) {
                recipient = Recipient.resolved(recipientDatabase.getByServiceId(aci).get());
            }
        } else {
            Log.w(TAG, "Registered number set had a null ACI!");
        }
    } else if (recipient.hasServiceId() && recipient.isRegistered() && hasCommunicatedWith(recipient)) {
        if (ApplicationDependencies.getSignalServiceAccountManager().isIdentifierRegistered(recipient.requireServiceId())) {
            recipientDatabase.markRegistered(recipient.getId(), recipient.requireServiceId());
        } else {
            recipientDatabase.markUnregistered(recipient.getId());
        }
        stopwatch.split("e164-unlisted-network");
    } else {
        recipientDatabase.markUnregistered(recipient.getId());
    }
    if (Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
        updateContactsDatabase(context, Collections.singletonList(recipient.getId()), false, result.getNumberRewrites());
    }
    newRegisteredState = result.getRegisteredNumbers().size() > 0 ? RegisteredState.REGISTERED : RegisteredState.NOT_REGISTERED;
    if (newRegisteredState != originalRegisteredState) {
        ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
        ApplicationDependencies.getJobManager().add(new StorageSyncJob());
        if (notifyOfNewUsers && newRegisteredState == RegisteredState.REGISTERED && recipient.resolve().isSystemContact()) {
            notifyNewUsers(context, Collections.singletonList(recipient.getId()));
        }
        StorageSyncHelper.scheduleSyncForDataChange();
    }
    stopwatch.split("e164-disk");
    stopwatch.stop(TAG);
    return newRegisteredState;
}
Also used : RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) MultiDeviceContactUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob) StorageSyncJob(org.thoughtcrime.securesms.jobs.StorageSyncJob) RegisteredState(org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState) ACI(org.whispersystems.signalservice.api.push.ACI) Stopwatch(org.thoughtcrime.securesms.util.Stopwatch) WorkerThread(androidx.annotation.WorkerThread)

Example 12 with RecipientDatabase

use of org.thoughtcrime.securesms.database.RecipientDatabase in project Signal-Android by WhisperSystems.

the class RetrieveProfileJob method enqueueRoutineFetchIfNecessary.

/**
 * Will fetch some profiles to ensure we're decently up-to-date if we haven't done so within a
 * certain time period.
 */
public static void enqueueRoutineFetchIfNecessary(Application application) {
    if (!SignalStore.registrationValues().isRegistrationComplete() || !SignalStore.account().isRegistered() || SignalStore.account().getAci() == null) {
        Log.i(TAG, "Registration not complete. Skipping.");
        return;
    }
    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 = SignalDatabase.recipients();
        long current = System.currentTimeMillis();
        List<RecipientId> ids = db.getRecipientsForRoutineProfileFetch(current - TimeUnit.DAYS.toMillis(30), current - TimeUnit.DAYS.toMillis(1), 50);
        ids.add(Recipient.self().getId());
        if (ids.size() > 0) {
            Log.i(TAG, "Optimistically refreshing " + ids.size() + " eligible recipient(s).");
            enqueue(new HashSet<>(ids));
        } else {
            Log.i(TAG, "No recipients to refresh.");
        }
        SignalStore.misc().setLastProfileRefreshTime(System.currentTimeMillis());
    });
}
Also used : RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId)

Example 13 with RecipientDatabase

use of org.thoughtcrime.securesms.database.RecipientDatabase in project Signal-Android by WhisperSystems.

the class RetrieveProfileJob method onRun.

@Override
public void onRun() throws IOException, RetryLaterException {
    if (!SignalStore.account().isRegistered()) {
        Log.w(TAG, "Unregistered. Skipping.");
        return;
    }
    Stopwatch stopwatch = new Stopwatch("RetrieveProfile");
    RecipientDatabase recipientDatabase = SignalDatabase.recipients();
    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");
    ProfileService profileService = new ProfileService(ApplicationDependencies.getGroupsV2Operations().getProfileOperations(), ApplicationDependencies.getSignalServiceMessageReceiver(), ApplicationDependencies.getSignalWebSocket());
    List<Observable<Pair<Recipient, ServiceResponse<ProfileAndCredential>>>> requests = Stream.of(recipients).filter(Recipient::hasServiceId).map(r -> ProfileUtil.retrieveProfile(context, r, getRequestType(r), profileService).toObservable()).toList();
    stopwatch.split("requests");
    OperationState operationState = Observable.mergeDelayError(requests).observeOn(Schedulers.io(), true).scan(new OperationState(), (state, pair) -> {
        Recipient recipient = pair.first();
        ProfileService.ProfileResponseProcessor processor = new ProfileService.ProfileResponseProcessor(pair.second());
        if (processor.hasResult()) {
            state.profiles.add(processor.getResult(recipient));
        } else if (processor.notFound()) {
            Log.w(TAG, "Failed to find a profile for " + recipient.getId());
            if (recipient.isRegistered()) {
                state.unregistered.add(recipient.getId());
            }
        } else if (processor.genericIoError()) {
            state.retries.add(recipient.getId());
        } else {
            Log.w(TAG, "Failed to retrieve profile for " + recipient.getId());
        }
        return state;
    }).lastOrError().blockingGet();
    stopwatch.split("responses");
    Set<RecipientId> success = SetUtil.difference(recipientIds, operationState.retries);
    Map<RecipientId, ServiceId> newlyRegistered = Stream.of(operationState.profiles).map(Pair::first).filterNot(Recipient::isRegistered).collect(Collectors.toMap(Recipient::getId, r -> r.getServiceId().orNull()));
    // noinspection SimplifyStreamApiCallChains
    Util.chunk(operationState.profiles, 150).stream().forEach(list -> {
        SignalDatabase.runInTransaction(() -> {
            for (Pair<Recipient, ProfileAndCredential> profile : list) {
                process(profile.first(), profile.second());
            }
        });
    });
    recipientDatabase.markProfilesFetched(success, System.currentTimeMillis());
    if (operationState.unregistered.size() > 0 || newlyRegistered.size() > 0) {
        Log.i(TAG, "Marking " + newlyRegistered.size() + " users as registered and " + operationState.unregistered.size() + " users as unregistered.");
        recipientDatabase.bulkUpdatedRegisteredStatus(newlyRegistered, operationState.unregistered);
    }
    stopwatch.split("process");
    for (Pair<Recipient, ProfileAndCredential> profile : operationState.profiles) {
        setIdentityKey(profile.first(), profile.second().getProfile().getIdentityKey());
    }
    stopwatch.split("identityKeys");
    long keyCount = Stream.of(operationState.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(), operationState.profiles.size(), keyCount, operationState.retries.size()));
    stopwatch.stop(TAG);
    recipientIds.clear();
    recipientIds.addAll(operationState.retries);
    if (recipientIds.size() > 0) {
        throw new RetryLaterException();
    }
}
Also used : SignalStore(org.thoughtcrime.securesms.keyvalue.SignalStore) NonNull(androidx.annotation.NonNull) Data(org.thoughtcrime.securesms.jobmanager.Data) JobManager(org.thoughtcrime.securesms.jobmanager.JobManager) RecipientUtil(org.thoughtcrime.securesms.recipients.RecipientUtil) ProfileKey(org.signal.zkgroup.profiles.ProfileKey) ProfileCipher(org.whispersystems.signalservice.api.crypto.ProfileCipher) ProfileAndCredential(org.whispersystems.signalservice.api.profiles.ProfileAndCredential) Badges(org.thoughtcrime.securesms.badges.Badges) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) Locale(java.util.Locale) Map(java.util.Map) Badge(org.thoughtcrime.securesms.badges.models.Badge) Recipient(org.thoughtcrime.securesms.recipients.Recipient) InvalidCiphertextException(org.whispersystems.signalservice.api.crypto.InvalidCiphertextException) SignalExecutors(org.signal.core.util.concurrent.SignalExecutors) UnidentifiedAccessMode(org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode) Base64(org.thoughtcrime.securesms.util.Base64) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) ProfileKeyUtil(org.thoughtcrime.securesms.crypto.ProfileKeyUtil) Set(java.util.Set) SetUtil(org.thoughtcrime.securesms.util.SetUtil) GroupDatabase(org.thoughtcrime.securesms.database.GroupDatabase) IdentityKey(org.whispersystems.libsignal.IdentityKey) Log(org.signal.core.util.logging.Log) List(java.util.List) Nullable(androidx.annotation.Nullable) ProfileService(org.whispersystems.signalservice.api.services.ProfileService) Application(android.app.Application) Job(org.thoughtcrime.securesms.jobmanager.Job) Context(android.content.Context) SignalDatabase(org.thoughtcrime.securesms.database.SignalDatabase) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) Stream(com.annimon.stream.Stream) Util(org.thoughtcrime.securesms.util.Util) ProfileName(org.thoughtcrime.securesms.profiles.ProfileName) WorkerThread(androidx.annotation.WorkerThread) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) InvalidKeyException(org.whispersystems.libsignal.InvalidKeyException) SignalServiceProfile(org.whispersystems.signalservice.api.profiles.SignalServiceProfile) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Schedulers(io.reactivex.rxjava3.schedulers.Schedulers) Pair(org.whispersystems.libsignal.util.Pair) ProfileUtil(org.thoughtcrime.securesms.util.ProfileUtil) Observable(io.reactivex.rxjava3.core.Observable) IdentityUtil(org.thoughtcrime.securesms.util.IdentityUtil) Collectors(com.annimon.stream.Collectors) ProfileKeyCredential(org.signal.zkgroup.profiles.ProfileKeyCredential) TextUtils(android.text.TextUtils) NetworkConstraint(org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint) IOException(java.io.IOException) ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) Optional(org.whispersystems.libsignal.util.guava.Optional) TimeUnit(java.util.concurrent.TimeUnit) Stopwatch(org.thoughtcrime.securesms.util.Stopwatch) ServiceId(org.whispersystems.signalservice.api.push.ServiceId) Collections(java.util.Collections) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) Stopwatch(org.thoughtcrime.securesms.util.Stopwatch) ProfileAndCredential(org.whispersystems.signalservice.api.profiles.ProfileAndCredential) Recipient(org.thoughtcrime.securesms.recipients.Recipient) Observable(io.reactivex.rxjava3.core.Observable) ServiceId(org.whispersystems.signalservice.api.push.ServiceId) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) ProfileService(org.whispersystems.signalservice.api.services.ProfileService) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) Pair(org.whispersystems.libsignal.util.Pair)

Example 14 with RecipientDatabase

use of org.thoughtcrime.securesms.database.RecipientDatabase in project Signal-Android by WhisperSystems.

the class MultiDeviceBlockedUpdateJob method onRun.

@Override
public void onRun() throws IOException, UntrustedIdentityException {
    if (!Recipient.self().isRegistered()) {
        throw new NotPushRegisteredException();
    }
    if (!TextSecurePreferences.isMultiDevice(context)) {
        Log.i(TAG, "Not multi device, aborting...");
        return;
    }
    RecipientDatabase database = SignalDatabase.recipients();
    try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) {
        List<SignalServiceAddress> blockedIndividuals = new LinkedList<>();
        List<byte[]> blockedGroups = new LinkedList<>();
        Recipient recipient;
        while ((recipient = reader.getNext()) != null) {
            if (recipient.isPushGroup()) {
                blockedGroups.add(recipient.requireGroupId().getDecodedId());
            } else if (recipient.isMaybeRegistered() && (recipient.hasServiceId() || recipient.hasE164())) {
                blockedIndividuals.add(RecipientUtil.toSignalServiceAddress(context, recipient));
            }
        }
        SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
        messageSender.sendSyncMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)), UnidentifiedAccessUtil.getAccessForSync(context));
    }
}
Also used : RecipientReader(org.thoughtcrime.securesms.database.RecipientDatabase.RecipientReader) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) NotPushRegisteredException(org.thoughtcrime.securesms.net.NotPushRegisteredException) SignalServiceMessageSender(org.whispersystems.signalservice.api.SignalServiceMessageSender) SignalServiceAddress(org.whispersystems.signalservice.api.push.SignalServiceAddress) Recipient(org.thoughtcrime.securesms.recipients.Recipient) BlockedListMessage(org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage) LinkedList(java.util.LinkedList)

Example 15 with RecipientDatabase

use of org.thoughtcrime.securesms.database.RecipientDatabase in project Signal-Android by WhisperSystems.

the class RefreshOwnProfileJob method setProfileKeyCredential.

private void setProfileKeyCredential(@NonNull Recipient recipient, @NonNull ProfileKey recipientProfileKey, @NonNull ProfileKeyCredential credential) {
    RecipientDatabase recipientDatabase = SignalDatabase.recipients();
    recipientDatabase.setProfileKeyCredential(recipient.getId(), recipientProfileKey, credential);
}
Also used : RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase)

Aggregations

RecipientDatabase (org.thoughtcrime.securesms.database.RecipientDatabase)72 Recipient (org.thoughtcrime.securesms.recipients.Recipient)32 WorkerThread (androidx.annotation.WorkerThread)30 RecipientId (org.thoughtcrime.securesms.recipients.RecipientId)20 NonNull (androidx.annotation.NonNull)18 IOException (java.io.IOException)14 Nullable (androidx.annotation.Nullable)10 ArrayList (java.util.ArrayList)10 Log (org.signal.core.util.logging.Log)10 SignalDatabase (org.thoughtcrime.securesms.database.SignalDatabase)10 ACI (org.whispersystems.signalservice.api.push.ACI)10 Context (android.content.Context)8 Stream (com.annimon.stream.Stream)8 Collections (java.util.Collections)8 List (java.util.List)8 TimeUnit (java.util.concurrent.TimeUnit)8 ProfileKey (org.signal.zkgroup.profiles.ProfileKey)8 ApplicationDependencies (org.thoughtcrime.securesms.dependencies.ApplicationDependencies)8 Optional (org.whispersystems.libsignal.util.guava.Optional)8 HashSet (java.util.HashSet)7