Support independent application migration versions.

This commit is contained in:
Greyson Parrelli 2019-09-05 14:39:18 -04:00
parent f81c0b448e
commit d1a6582ad7
11 changed files with 158 additions and 63 deletions

View file

@ -36,6 +36,7 @@ import org.signal.aesgcmprovider.AesGcmProvider;
import org.thoughtcrime.securesms.components.TypingStatusRepository; import org.thoughtcrime.securesms.components.TypingStatusRepository;
import org.thoughtcrime.securesms.components.TypingStatusSender; import org.thoughtcrime.securesms.components.TypingStatusSender;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider; import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider;
import org.thoughtcrime.securesms.gcm.FcmJobService; import org.thoughtcrime.securesms.gcm.FcmJobService;
@ -69,6 +70,7 @@ import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener; import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.VersionTracker;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper; import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
import org.webrtc.PeerConnectionFactory; import org.webrtc.PeerConnectionFactory;
import org.webrtc.PeerConnectionFactory.InitializationOptions; import org.webrtc.PeerConnectionFactory.InitializationOptions;
@ -94,7 +96,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
private static final String TAG = ApplicationContext.class.getSimpleName(); private static final String TAG = ApplicationContext.class.getSimpleName();
private ExpiringMessageManager expiringMessageManager; private ExpiringMessageManager expiringMessageManager;
private ViewOnceMessageManager viewOnceMessageManager; private ViewOnceMessageManager viewOnceMessageManager;
private TypingStatusRepository typingStatusRepository; private TypingStatusRepository typingStatusRepository;
private TypingStatusSender typingStatusSender; private TypingStatusSender typingStatusSender;
private JobManager jobManager; private JobManager jobManager;
@ -114,6 +116,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
initializeSecurityProvider(); initializeSecurityProvider();
initializeLogging(); initializeLogging();
initializeCrashHandling(); initializeCrashHandling();
initializeFirstEverAppLaunch();
initializeAppDependencies(); initializeAppDependencies();
initializeJobManager(); initializeJobManager();
initializeApplicationMigrations(); initializeApplicationMigrations();
@ -223,7 +226,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
.setConstraintFactories(JobManagerFactories.getConstraintFactories(this)) .setConstraintFactories(JobManagerFactories.getConstraintFactories(this))
.setConstraintObservers(JobManagerFactories.getConstraintObservers(this)) .setConstraintObservers(JobManagerFactories.getConstraintObservers(this))
.setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(this))) .setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(this)))
.setJobMigrator(new JobMigrator(TextSecurePreferences.getJobManagerVersion(this), 2, JobManagerFactories.getJobMigrations(this))) .setJobMigrator(new JobMigrator(TextSecurePreferences.getJobManagerVersion(this), JobManager.CURRENT_VERSION, JobManagerFactories.getJobMigrations(this)))
.build()); .build());
} }
@ -239,6 +242,20 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
ApplicationDependencies.init(this, new ApplicationDependencyProvider(this, new SignalServiceNetworkAccess(this))); ApplicationDependencies.init(this, new ApplicationDependencyProvider(this, new SignalServiceNetworkAccess(this)));
} }
private void initializeFirstEverAppLaunch() {
if (TextSecurePreferences.getFirstInstallVersion(this) == -1) {
if (!SQLCipherOpenHelper.databaseFileExists(this)) {
Log.i(TAG, "First ever app launch!");
TextSecurePreferences.setAppMigrationVersion(this, ApplicationMigrations.CURRENT_VERSION);
TextSecurePreferences.setJobManagerVersion(this, JobManager.CURRENT_VERSION);
}
Log.i(TAG, "Setting first install version to " + BuildConfig.CANONICAL_VERSION_CODE);
TextSecurePreferences.setFirstInstallVersion(this, BuildConfig.CANONICAL_VERSION_CODE);
}
}
private void initializeGcmCheck() { private void initializeGcmCheck() {
if (TextSecurePreferences.isPushRegistered(this)) { if (TextSecurePreferences.isPushRegistered(this)) {
long nextSetTime = TextSecurePreferences.getFcmTokenLastSetTime(this) + TimeUnit.HOURS.toMillis(6); long nextSetTime = TextSecurePreferences.getFcmTokenLastSetTime(this) + TimeUnit.HOURS.toMillis(6);

View file

@ -30,7 +30,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
private static final int STATE_NORMAL = 0; private static final int STATE_NORMAL = 0;
private static final int STATE_CREATE_PASSPHRASE = 1; private static final int STATE_CREATE_PASSPHRASE = 1;
private static final int STATE_PROMPT_PASSPHRASE = 2; private static final int STATE_PROMPT_PASSPHRASE = 2;
private static final int STATE_UPGRADE_DATABASE = 3; private static final int STATE_UI_BLOCKING_UPGRADE = 3;
private static final int STATE_PROMPT_PUSH_REGISTRATION = 4; private static final int STATE_PROMPT_PUSH_REGISTRATION = 4;
private static final int STATE_EXPERIENCE_UPGRADE = 5; private static final int STATE_EXPERIENCE_UPGRADE = 5;
private static final int STATE_WELCOME_SCREEN = 6; private static final int STATE_WELCOME_SCREEN = 6;
@ -133,7 +133,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
switch (state) { switch (state) {
case STATE_CREATE_PASSPHRASE: return getCreatePassphraseIntent(); case STATE_CREATE_PASSPHRASE: return getCreatePassphraseIntent();
case STATE_PROMPT_PASSPHRASE: return getPromptPassphraseIntent(); case STATE_PROMPT_PASSPHRASE: return getPromptPassphraseIntent();
case STATE_UPGRADE_DATABASE: return getUpgradeDatabaseIntent(); case STATE_UI_BLOCKING_UPGRADE: return getUiBlockingUpgradeIntent();
case STATE_WELCOME_SCREEN: return getWelcomeIntent(); case STATE_WELCOME_SCREEN: return getWelcomeIntent();
case STATE_PROMPT_PUSH_REGISTRATION: return getPushRegistrationIntent(); case STATE_PROMPT_PUSH_REGISTRATION: return getPushRegistrationIntent();
case STATE_EXPERIENCE_UPGRADE: return getExperienceUpgradeIntent(); case STATE_EXPERIENCE_UPGRADE: return getExperienceUpgradeIntent();
@ -146,8 +146,8 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
return STATE_CREATE_PASSPHRASE; return STATE_CREATE_PASSPHRASE;
} else if (locked) { } else if (locked) {
return STATE_PROMPT_PASSPHRASE; return STATE_PROMPT_PASSPHRASE;
} else if (ApplicationMigrations.isUpdate(this)) { } else if (ApplicationMigrations.isUpdate(this) && ApplicationMigrations.isUiBlockingMigrationRunning()) {
return STATE_UPGRADE_DATABASE; return STATE_UI_BLOCKING_UPGRADE;
} else if (!TextSecurePreferences.hasSeenWelcomeScreen(this)) { } else if (!TextSecurePreferences.hasSeenWelcomeScreen(this)) {
return STATE_WELCOME_SCREEN; return STATE_WELCOME_SCREEN;
} else if (!TextSecurePreferences.hasPromptedPushRegistration(this)) { } else if (!TextSecurePreferences.hasPromptedPushRegistration(this)) {
@ -167,7 +167,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
return getRoutedIntent(PassphrasePromptActivity.class, getIntent()); return getRoutedIntent(PassphrasePromptActivity.class, getIntent());
} }
private Intent getUpgradeDatabaseIntent() { private Intent getUiBlockingUpgradeIntent() {
return getRoutedIntent(ApplicationMigrationActivity.class, return getRoutedIntent(ApplicationMigrationActivity.class,
TextSecurePreferences.hasPromptedPushRegistration(this) TextSecurePreferences.hasPromptedPushRegistration(this)
? getConversationListIntent() ? getConversationListIntent()

View file

@ -535,6 +535,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.setVersion(DATABASE_VERSION); db.setVersion(DATABASE_VERSION);
} }
public static boolean databaseFileExists(@NonNull Context context) {
return context.getDatabasePath(DATABASE_NAME).exists();
}
private void executeStatements(SQLiteDatabase db, String[] statements) { private void executeStatements(SQLiteDatabase db, String[] statements) {
for (String statement : statements) for (String statement : statements)
db.execSQL(statement); db.execSQL(statement);

View file

@ -261,7 +261,7 @@ class JobController {
JobSpec jobSpec = new JobSpec(job.getId(), JobSpec jobSpec = new JobSpec(job.getId(),
job.getFactoryKey(), job.getFactoryKey(),
job.getParameters().getQueue(), job.getParameters().getQueue(),
job.getParameters().getCreateTime(), System.currentTimeMillis(),
job.getNextRunAttemptTime(), job.getNextRunAttemptTime(),
job.getRunAttempt(), job.getRunAttempt(),
job.getParameters().getMaxAttempts(), job.getParameters().getMaxAttempts(),

View file

@ -33,6 +33,8 @@ public class JobManager implements ConstraintObserver.Notifier {
private static final String TAG = JobManager.class.getSimpleName(); private static final String TAG = JobManager.class.getSimpleName();
public static final int CURRENT_VERSION = 2;
private final Application application; private final Application application;
private final Configuration configuration; private final Configuration configuration;
private final ExecutorService executor; private final ExecutorService executor;

View file

@ -93,7 +93,7 @@ public class FastJobStorage implements JobStorage {
public synchronized @NonNull List<JobSpec> getPendingJobsWithNoDependenciesInCreatedOrder(long currentTime) { public synchronized @NonNull List<JobSpec> getPendingJobsWithNoDependenciesInCreatedOrder(long currentTime) {
Optional<JobSpec> migrationJob = getMigrationJob(); Optional<JobSpec> migrationJob = getMigrationJob();
if (migrationJob.isPresent() && !migrationJob.get().isRunning()) { if (migrationJob.isPresent() && !migrationJob.get().isRunning() && migrationJob.get().getNextRunAttemptTime() <= currentTime) {
return Collections.singletonList(migrationJob.get()); return Collections.singletonList(migrationJob.get());
} else if (migrationJob.isPresent()) { } else if (migrationJob.isPresent()) {
return Collections.emptyList(); return Collections.emptyList();

View file

@ -59,6 +59,7 @@ import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.logsubmit.util.Scrubber; import org.thoughtcrime.securesms.logsubmit.util.Scrubber;
import org.thoughtcrime.securesms.util.BucketInfo; import org.thoughtcrime.securesms.util.BucketInfo;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
@ -513,19 +514,19 @@ public class SubmitLogFragment extends Fragment {
final PackageManager pm = context.getPackageManager(); final PackageManager pm = context.getPackageManager();
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
builder.append("Time : ").append(System.currentTimeMillis()).append('\n'); builder.append("Time : ").append(System.currentTimeMillis()).append('\n');
builder.append("Device : ") builder.append("Device : ").append(Build.MANUFACTURER).append(" ")
.append(Build.MANUFACTURER).append(" ") .append(Build.MODEL).append(" (")
.append(Build.MODEL).append(" (") .append(Build.PRODUCT).append(")\n");
.append(Build.PRODUCT).append(")\n"); builder.append("Android : ").append(VERSION.RELEASE).append(" (")
builder.append("Android : ").append(VERSION.RELEASE).append(" (") .append(VERSION.INCREMENTAL).append(", ")
.append(VERSION.INCREMENTAL).append(", ") .append(Build.DISPLAY).append(")\n");
.append(Build.DISPLAY).append(")\n"); builder.append("ABIs : ").append(TextUtils.join(", ", getSupportedAbis())).append("\n");
builder.append("ABIs : ").append(TextUtils.join(", ", getSupportedAbis())).append("\n"); builder.append("Memory : ").append(getMemoryUsage(context)).append("\n");
builder.append("Memory : ").append(getMemoryUsage(context)).append("\n"); builder.append("Memclass : ").append(getMemoryClass(context)).append("\n");
builder.append("Memclass: ").append(getMemoryClass(context)).append("\n"); builder.append("OS Host : ").append(Build.HOST).append("\n");
builder.append("OS Host : ").append(Build.HOST).append("\n"); builder.append("First Version: ").append(TextSecurePreferences.getFirstInstallVersion(context)).append("\n");
builder.append("App : "); builder.append("App : ");
try { try {
builder.append(pm.getApplicationLabel(pm.getApplicationInfo(context.getPackageName(), 0))) builder.append(pm.getApplicationLabel(pm.getApplicationInfo(context.getPackageName(), 0)))
.append(" ") .append(" ")

View file

@ -35,7 +35,7 @@ public class ApplicationMigrationActivity extends BaseActivity {
public void onCreate(Bundle bundle) { public void onCreate(Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
ApplicationMigrations.isUiBlockingMigrationRunning().observe(this, running -> { ApplicationMigrations.getUiBlockingMigrationStatus().observe(this, running -> {
if (running == null) { if (running == null) {
return; return;
} }

View file

@ -6,19 +6,17 @@ import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import com.annimon.stream.Stream;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode; import org.greenrobot.eventbus.ThreadMode;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.VersionTracker; import org.thoughtcrime.securesms.util.VersionTracker;
import java.util.LinkedList; import java.util.LinkedHashMap;
import java.util.List; import java.util.Map;
/** /**
* Manages application-level migrations. * Manages application-level migrations.
@ -38,10 +36,14 @@ public class ApplicationMigrations {
private static final MutableLiveData<Boolean> UI_BLOCKING_MIGRATION_RUNNING = new MutableLiveData<>(); private static final MutableLiveData<Boolean> UI_BLOCKING_MIGRATION_RUNNING = new MutableLiveData<>();
private static final int LEGACY_CANONICAL_VERSION = 455;
public static final int CURRENT_VERSION = 3;
private static final class Version { private static final class Version {
static final int LEGACY = 455; static final int LEGACY = 1;
static final int RECIPIENT_ID = 525; // TODO [greyson] USE PROPER APPLICATION VERSION static final int RECIPIENT_ID = 2;
static final int RECIPIENT_SEARCH = 525; // TODO [greyson] USE PROPER APPLICATION VERSION static final int RECIPIENT_SEARCH = 3;
} }
/** /**
@ -50,58 +52,80 @@ public class ApplicationMigrations {
* executing before we add the migration jobs. * executing before we add the migration jobs.
*/ */
public static void onApplicationCreate(@NonNull Context context, @NonNull JobManager jobManager) { public static void onApplicationCreate(@NonNull Context context, @NonNull JobManager jobManager) {
if (isLegacyUpdate(context)) {
Log.i(TAG, "Detected the need for a legacy update. Last seen canonical version: " + VersionTracker.getLastSeenVersion(context));
TextSecurePreferences.setAppMigrationVersion(context, 0);
}
if (!isUpdate(context)) { if (!isUpdate(context)) {
Log.d(TAG, "Not an update. Skipping."); Log.d(TAG, "Not an update. Skipping.");
return; return;
} }
final int currentVersion = Util.getCanonicalVersionCode(); final int lastSeenVersion = TextSecurePreferences.getAppMigrationVersion(context);
final int lastSeenVersion = VersionTracker.getLastSeenVersion(context); Log.d(TAG, "currentVersion: " + CURRENT_VERSION + ", lastSeenVersion: " + lastSeenVersion);
Log.d(TAG, "currentVersion: " + currentVersion + " lastSeenVersion: " + lastSeenVersion); LinkedHashMap<Integer, MigrationJob> migrationJobs = getMigrationJobs(context, lastSeenVersion);
List<MigrationJob> migrationJobs = getMigrationJobs(context, lastSeenVersion);
if (migrationJobs.size() > 0) { if (migrationJobs.size() > 0) {
Log.i(TAG, "About to enqueue " + migrationJobs.size() + " migration(s)."); Log.i(TAG, "About to enqueue " + migrationJobs.size() + " migration(s).");
boolean uiBlocking = Stream.of(migrationJobs).reduce(false, (existing, job) -> existing || job.isUiBlocking()); boolean uiBlocking = true;
UI_BLOCKING_MIGRATION_RUNNING.postValue(uiBlocking); int uiBlockingVersion = lastSeenVersion;
if (uiBlocking) { for (Map.Entry<Integer, MigrationJob> entry : migrationJobs.entrySet()) {
Log.i(TAG, "Migration set is UI-blocking."); int version = entry.getKey();
MigrationJob job = entry.getValue();
uiBlocking &= job.isUiBlocking();
if (uiBlocking) {
uiBlockingVersion = version;
}
jobManager.add(job);
jobManager.add(new MigrationCompleteJob(version));
}
if (uiBlockingVersion > lastSeenVersion) {
Log.i(TAG, "Migration set is UI-blocking through version " + uiBlockingVersion + ".");
UI_BLOCKING_MIGRATION_RUNNING.setValue(true);
} else { } else {
Log.i(TAG, "Migration set is non-UI-blocking."); Log.i(TAG, "Migration set is non-UI-blocking.");
UI_BLOCKING_MIGRATION_RUNNING.setValue(false);
} }
for (MigrationJob job : migrationJobs) {
jobManager.add(job);
}
jobManager.add(new MigrationCompleteJob(currentVersion));
final long startTime = System.currentTimeMillis(); final long startTime = System.currentTimeMillis();
final int uiVersion = uiBlockingVersion;
EventBus.getDefault().register(new Object() { EventBus.getDefault().register(new Object() {
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN) @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onMigrationComplete(MigrationCompleteEvent event) { public void onMigrationComplete(MigrationCompleteEvent event) {
Log.i(TAG, "Received MigrationCompleteEvent for version " + event.getVersion() + "."); Log.i(TAG, "Received MigrationCompleteEvent for version " + event.getVersion() + ". (Current: " + CURRENT_VERSION + ")");
if (event.getVersion() == currentVersion) { if (event.getVersion() > CURRENT_VERSION) {
throw new AssertionError("Received a higher version than the current version? App downgrades are not supported. (received: " + event.getVersion() + ", current: " + CURRENT_VERSION + ")");
}
Log.i(TAG, "Updating last migration version to " + event.getVersion());
TextSecurePreferences.setAppMigrationVersion(context, event.getVersion());
if (event.getVersion() == CURRENT_VERSION) {
Log.i(TAG, "Migration complete. Took " + (System.currentTimeMillis() - startTime) + " ms."); Log.i(TAG, "Migration complete. Took " + (System.currentTimeMillis() - startTime) + " ms.");
EventBus.getDefault().unregister(this); EventBus.getDefault().unregister(this);
VersionTracker.updateLastSeenVersion(context); VersionTracker.updateLastSeenVersion(context);
UI_BLOCKING_MIGRATION_RUNNING.postValue(false); UI_BLOCKING_MIGRATION_RUNNING.setValue(false);
} else { } else if (event.getVersion() >= uiVersion) {
Log.i(TAG, "Version doesn't match. Looking for " + currentVersion + ", but received " + event.getVersion() + "."); Log.i(TAG, "Version is >= the UI-blocking version. Posting 'false'.");
UI_BLOCKING_MIGRATION_RUNNING.setValue(false);
} }
} }
}); });
} else { } else {
Log.d(TAG, "No migrations."); Log.d(TAG, "No migrations.");
TextSecurePreferences.setAppMigrationVersion(context, CURRENT_VERSION);
VersionTracker.updateLastSeenVersion(context); VersionTracker.updateLastSeenVersion(context);
UI_BLOCKING_MIGRATION_RUNNING.postValue(false); UI_BLOCKING_MIGRATION_RUNNING.setValue(false);
} }
} }
@ -109,36 +133,45 @@ public class ApplicationMigrations {
* @return A {@link LiveData} object that will update with whether or not a UI blocking migration * @return A {@link LiveData} object that will update with whether or not a UI blocking migration
* is in progress. * is in progress.
*/ */
public static LiveData<Boolean> isUiBlockingMigrationRunning() { public static LiveData<Boolean> getUiBlockingMigrationStatus() {
return UI_BLOCKING_MIGRATION_RUNNING; return UI_BLOCKING_MIGRATION_RUNNING;
} }
/**
* @return True if a UI blocking migration is running.
*/
public static boolean isUiBlockingMigrationRunning() {
Boolean value = UI_BLOCKING_MIGRATION_RUNNING.getValue();
return value != null && value;
}
/** /**
* @return Whether or not we're in the middle of an update, as determined by the last seen and * @return Whether or not we're in the middle of an update, as determined by the last seen and
* current version. * current version.
*/ */
public static boolean isUpdate(Context context) { public static boolean isUpdate(@NonNull Context context) {
int currentVersionCode = Util.getCanonicalVersionCode(); return isLegacyUpdate(context) || TextSecurePreferences.getAppMigrationVersion(context) < CURRENT_VERSION;
int previousVersionCode = VersionTracker.getLastSeenVersion(context);
return previousVersionCode < currentVersionCode;
} }
private static List<MigrationJob> getMigrationJobs(@NonNull Context context, int lastSeenVersion) { private static LinkedHashMap<Integer, MigrationJob> getMigrationJobs(@NonNull Context context, int lastSeenVersion) {
List<MigrationJob> jobs = new LinkedList<>(); LinkedHashMap<Integer, MigrationJob> jobs = new LinkedHashMap<>();
if (lastSeenVersion < Version.LEGACY) { if (lastSeenVersion < Version.LEGACY) {
jobs.add(new LegacyMigrationJob()); jobs.put(Version.LEGACY, new LegacyMigrationJob());
} }
if (lastSeenVersion < Version.RECIPIENT_ID) { if (lastSeenVersion < Version.RECIPIENT_ID) {
jobs.add(new DatabaseMigrationJob()); jobs.put(Version.RECIPIENT_ID, new DatabaseMigrationJob());
} }
if (lastSeenVersion < Version.RECIPIENT_SEARCH) { if (lastSeenVersion < Version.RECIPIENT_SEARCH) {
jobs.add(new RecipientSearchMigrationJob()); jobs.put(Version.RECIPIENT_SEARCH, new RecipientSearchMigrationJob());
} }
return jobs; return jobs;
} }
private static boolean isLegacyUpdate(@NonNull Context context) {
return VersionTracker.getLastSeenVersion(context) < LEGACY_CANONICAL_VERSION;
}
} }

View file

@ -189,6 +189,10 @@ public class TextSecurePreferences {
private static final String JOB_MANAGER_VERSION = "pref_job_manager_version"; private static final String JOB_MANAGER_VERSION = "pref_job_manager_version";
private static final String APP_MIGRATION_VERSION = "pref_app_migration_version";
private static final String FIRST_INSTALL_VERSION = "pref_first_install_version";
public static boolean isScreenLockEnabled(@NonNull Context context) { public static boolean isScreenLockEnabled(@NonNull Context context) {
return getBooleanPreference(context, SCREEN_LOCK, false); return getBooleanPreference(context, SCREEN_LOCK, false);
} }
@ -1128,6 +1132,23 @@ public class TextSecurePreferences {
return getIntegerPreference(contex, JOB_MANAGER_VERSION, 1); return getIntegerPreference(contex, JOB_MANAGER_VERSION, 1);
} }
public static void setAppMigrationVersion(Context context, int version) {
setIntegerPrefrence(context, APP_MIGRATION_VERSION, version);
}
public static int getAppMigrationVersion(Context context) {
return getIntegerPreference(context, APP_MIGRATION_VERSION, 1);
}
public static void setFirstInstallVersion(Context context, int version) {
setIntegerPrefrence(context, FIRST_INSTALL_VERSION, version);
}
public static int getFirstInstallVersion(Context context) {
return getIntegerPreference(context, FIRST_INSTALL_VERSION, -1);
}
public static void setBooleanPreference(Context context, String key, boolean value) { public static void setBooleanPreference(Context context, String key, boolean value) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply(); PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply();
} }

View file

@ -380,6 +380,23 @@ public class FastJobStorageTest {
assertEquals("1", jobs.get(0).getId()); assertEquals("1", jobs.get(0).getId());
} }
@Test
public void getPendingJobsWithNoDependenciesInCreatedOrder_onlyMigrationJobWithAppropriateNextRunTime() {
FullSpec migrationSpec1 = new FullSpec(new JobSpec("1", "f1", Job.Parameters.MIGRATION_QUEUE_KEY, 0, 999, 0, 0, 0, -1, -1, EMPTY_DATA, false),
Collections.emptyList(),
Collections.emptyList());
FullSpec migrationSpec2 = new FullSpec(new JobSpec("2", "f2", Job.Parameters.MIGRATION_QUEUE_KEY, 5, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
Collections.emptyList(),
Collections.emptyList());
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(migrationSpec1, migrationSpec2)));
subject.init();
List<JobSpec> jobs = subject.getPendingJobsWithNoDependenciesInCreatedOrder(10);
assertTrue(jobs.isEmpty());
}
@Test @Test
public void deleteJobs_writesToDatabase() { public void deleteJobs_writesToDatabase() {
JobDatabase database = noopDatabase(); JobDatabase database = noopDatabase();