kopia lustrzana https://github.com/ryukoposting/Signal-Android
104 wiersze
4.3 KiB
Java
104 wiersze
4.3 KiB
Java
package org.thoughtcrime.securesms.storage;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import org.signal.core.util.logging.Log;
|
|
import org.whispersystems.signalservice.api.storage.SignalRecord;
|
|
|
|
import java.io.IOException;
|
|
import java.util.Collection;
|
|
import java.util.Comparator;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.TreeSet;
|
|
|
|
/**
|
|
* An implementation of {@link StorageRecordProcessor} that solidifies a pattern and reduces
|
|
* duplicate code in individual implementations.
|
|
*
|
|
* Concerning the implementation of {@link #compare(Object, Object)}, it's purpose is to detect if
|
|
* two items would map to the same logical entity (i.e. they would correspond to the same record in
|
|
* our local store). We use it for a {@link TreeSet}, so mainly it's just important that the '0'
|
|
* case is correct. Other cases are whatever, just make it something stable.
|
|
*/
|
|
abstract class DefaultStorageRecordProcessor<E extends SignalRecord> implements StorageRecordProcessor<E>, Comparator<E> {
|
|
|
|
private static final String TAG = Log.tag(DefaultStorageRecordProcessor.class);
|
|
|
|
/**
|
|
* One type of invalid remote data this handles is two records mapping to the same local data. We
|
|
* have to trim this bad data out, because if we don't, we'll upload an ID set that only has one
|
|
* of the IDs in it, but won't properly delete the dupes, which will then fail our validation
|
|
* checks.
|
|
*
|
|
* This is a bit tricky -- as we process records, ID's are written back to the local store, so we
|
|
* can't easily be like "oh multiple records are mapping to the same local storage ID". And in
|
|
* general we rely on SignalRecords to implement an equals() that includes the StorageId, so using
|
|
* a regular set is out. Instead, we use a {@link TreeSet}, which allows us to define a custom
|
|
* comparator for checking equality. Then we delegate to the subclass to tell us if two items are
|
|
* the same based on their actual data (i.e. two contacts having the same UUID, or two groups
|
|
* having the same MasterKey).
|
|
*/
|
|
@Override
|
|
public void process(@NonNull Collection<E> remoteRecords, @NonNull StorageKeyGenerator keyGenerator) throws IOException {
|
|
Set<E> matchedRecords = new TreeSet<>(this);
|
|
int i = 0;
|
|
|
|
for (E remote : remoteRecords) {
|
|
if (isInvalid(remote)) {
|
|
warn(i, remote, "Found invalid key! Ignoring it.");
|
|
} else {
|
|
Optional<E> local = getMatching(remote, keyGenerator);
|
|
|
|
if (local.isPresent()) {
|
|
E merged = merge(remote, local.get(), keyGenerator);
|
|
|
|
if (matchedRecords.contains(local.get())) {
|
|
warn(i, remote, "Multiple remote records map to the same local record! Ignoring this one.");
|
|
} else {
|
|
matchedRecords.add(local.get());
|
|
|
|
if (!merged.equals(remote)) {
|
|
info(i, remote, "[Remote Update] " + new StorageRecordUpdate<>(remote, merged).toString());
|
|
}
|
|
|
|
if (!merged.equals(local.get())) {
|
|
StorageRecordUpdate<E> update = new StorageRecordUpdate<>(local.get(), merged);
|
|
info(i, remote, "[Local Update] " + update.toString());
|
|
updateLocal(update);
|
|
}
|
|
}
|
|
} else {
|
|
info(i, remote, "No matching local record. Inserting.");
|
|
insertLocal(remote);
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
private void info(int i, E record, String message) {
|
|
Log.i(TAG, "[" + i + "][" + record.getClass().getSimpleName() + "] " + message);
|
|
}
|
|
|
|
private void warn(int i, E record, String message) {
|
|
Log.w(TAG, "[" + i + "][" + record.getClass().getSimpleName() + "] " + message);
|
|
}
|
|
|
|
/**
|
|
* @return True if the record is invalid and should be removed from storage service, otherwise false.
|
|
*/
|
|
abstract boolean isInvalid(@NonNull E remote);
|
|
|
|
/**
|
|
* Only records that pass the validity check (i.e. return false from {@link #isInvalid(SignalRecord)}
|
|
* make it to here, so you can assume all records are valid.
|
|
*/
|
|
abstract @NonNull Optional<E> getMatching(@NonNull E remote, @NonNull StorageKeyGenerator keyGenerator);
|
|
|
|
abstract @NonNull E merge(@NonNull E remote, @NonNull E local, @NonNull StorageKeyGenerator keyGenerator);
|
|
abstract void insertLocal(@NonNull E record) throws IOException;
|
|
abstract void updateLocal(@NonNull StorageRecordUpdate<E> update);
|
|
}
|