library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_BACKOFF_POLICY = "backoffPolicy";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_REQUIRES_DEVICE_IDLE = "requiresDeviceIdle";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_BACKOFF_MS = "backoffMs";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_REQUIREMENTS_ENFORCED = "requirementsEnforced";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_REQUIRES_BATTERY_NOT_LOW = "requiresBatteryNotLow";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_EXACT = "exact";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_TAG = "tag";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_STARTED = "started";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_FLEX_SUPPORT = "flexSupport";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_INTERVAL_MS = "intervalMs";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_LAST_RUN = "lastRun";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_REQUIRES_CHARGING = "requiresCharging";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_SCHEDULED_AT = "scheduledAt";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_REQUIRES_STORAGE_NOT_LOW = "requiresStorageNotLow";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_TRANSIENT = "transient";
library/src/main/java/com/evernote/android/job/util/support/PersistableBundleCompat.java
@SuppressWarnings("unused")
public final class PersistableBundleCompat {

    private static final JobCat CAT = new JobCat("PersistableBundleCompat");
    private static final String UTF_8 = "UTF-8";

    private final Map<String, Object> mValues;

    public PersistableBundleCompat() {
        this(new HashMap<String, Object>());
    }

    public PersistableBundleCompat(PersistableBundleCompat bundle) {
        this(new HashMap<>(bundle.mValues));
    }

    private PersistableBundleCompat(Map<String, Object> values) {
        mValues = values;
    }

    public void putBoolean(String key, boolean value) {
        mValues.put(key, value);
    }

    public boolean getBoolean(String key, boolean defaultValue) {
        Object value = mValues.get(key);
        if (value instanceof Boolean) {
            return (Boolean) value;
        } else {
            return defaultValue;
        }
    }

    public void putInt(String key, int value) {
        mValues.put(key, value);
    }

    public int getInt(String key, int defaultValue) {
        Object value = mValues.get(key);
        if (value instanceof Integer) {
            return (Integer) value;
        } else {
            return defaultValue;
        }
    }

    public void putIntArray(String key, int[] value) {
        mValues.put(key, value);
    }

    public int[] getIntArray(String key) {
        Object value = mValues.get(key);
        if (value instanceof int[]) {
            return (int[]) value;
        } else {
            return null;
        }
    }

    public void putLong(String key, long value) {
        mValues.put(key, value);
    }

    public long getLong(String key, long defaultValue) {
        Object value = mValues.get(key);
        if (value instanceof Long) {
            return (Long) value;
        } else {
            return defaultValue;
        }
    }

    public void putLongArray(String key, long[] value) {
        mValues.put(key, value);
    }

    public long[] getLongArray(String key) {
        Object value = mValues.get(key);
        if (value instanceof long[]) {
            return (long[]) value;
        } else {
            return null;
        }
    }

    public void putDouble(String key, double value) {
        mValues.put(key, value);
    }

    public double getDouble(String key, double defaultValue) {
        Object value = mValues.get(key);
        if (value instanceof Double) {
            return (Double) value;
        } else {
            return defaultValue;
        }
    }

    public void putDoubleArray(String key, double[] value) {
        mValues.put(key, value);
    }

    public double[] getDoubleArray(String key) {
        Object value = mValues.get(key);
        if (value instanceof double[]) {
            return (double[]) value;
        } else {
            return null;
        }
    }

    public void putString(String key, String value) {
        mValues.put(key, value);
    }

    public String getString(String key, String defaultValue) {
        Object value = mValues.get(key);
        if (value instanceof String) {
            return (String) value;
        } else {
            return defaultValue;
        }
    }

    public void putStringArray(String key, String[] value) {
        mValues.put(key, value);
    }

    public String[] getStringArray(String key) {
        Object value = mValues.get(key);
        if (value instanceof String[]) {
            return (String[]) value;
        } else {
            return null;
        }
    }

    public void putPersistableBundleCompat(String key, PersistableBundleCompat value) {
        mValues.put(key, value == null ? null : value.mValues);
    }

    @SuppressWarnings("unchecked")
    public PersistableBundleCompat getPersistableBundleCompat(String key) {
        Object value = mValues.get(key);
        if (value instanceof Map) {
            return new PersistableBundleCompat((Map<String, Object>) value);
        } else {
            return null;
        }
    }

    public void clear() {
        mValues.clear();
    }

    public boolean containsKey(String key) {
        return mValues.containsKey(key);
    }

    public Object get(String key) {
        return mValues.get(key);
    }

    public boolean isEmpty() {
        return mValues.isEmpty();
    }

    public Set<String> keySet() {
        return mValues.keySet();
    }

    public void putAll(PersistableBundleCompat bundle) {
        mValues.putAll(bundle.mValues);
    }

    public void remove(String key) {
        mValues.remove(key);
    }

    public int size() {
        return mValues.size();
    }

    @NonNull
    public String saveToXml() {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            XmlUtils.writeMapXml(mValues, outputStream);
            return outputStream.toString(UTF_8);

        } catch (XmlPullParserException | IOException e) {
            CAT.e(e);
            // shouldn't happen
            return "";

        } catch (Error e) {
            // https://gist.github.com/vRallev/9444359f05259e4b6317 and other crashes on rooted devices
            CAT.e(e);
            return "";

        } finally {
            try {
                outputStream.close();
            } catch (IOException ignored) {
            }
        }
    }

    @SuppressWarnings("unchecked")
    @NonNull
    public static PersistableBundleCompat fromXml(@NonNull String xml) {
        ByteArrayInputStream inputStream = null;
        try {
            inputStream = new ByteArrayInputStream(xml.getBytes(UTF_8));
            HashMap<String, ?> map = XmlUtils.readMapXml(inputStream);
            return new PersistableBundleCompat((Map<String, Object>) map);

        } catch (XmlPullParserException | IOException e) {
            CAT.e(e);
            return new PersistableBundleCompat();

        } catch (VerifyError e) {
            // https://gist.github.com/vRallev/9444359f05259e4b6317
            CAT.e(e);
            return new PersistableBundleCompat();

        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException ignored) {
                }
            }
        }
    }
}
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String DATABASE_NAME = PREF_FILE_NAME + ".db";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_NUM_FAILURES = "numFailures";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_START_MS = "startMs";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_EXTRAS = "extras";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_END_MS = "endMs";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_FLEX_MS = "flexMs";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_ID = "_id";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String COLUMN_NETWORK_TYPE = "networkType";
library/src/main/java/com/evernote/android/job/JobStorage.java
public static final String JOB_TABLE_NAME = "jobs";


Question: package com.evernote.android.job;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import com.evernote.android.job.test.JobRobolectricTestRunner;
import com.evernote.android.job.util.support.PersistableBundleCompat;
import java.util.concurrent.TimeUnit;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import static com.evernote.android.job.JobStorage.COLUMN_BACKOFF_MS;
import static com.evernote.android.job.JobStorage.COLUMN_BACKOFF_POLICY;
import static com.evernote.android.job.JobStorage.COLUMN_END_MS;
import static com.evernote.android.job.JobStorage.COLUMN_EXACT;
import static com.evernote.android.job.JobStorage.COLUMN_EXTRAS;
import static com.evernote.android.job.JobStorage.COLUMN_FLEX_MS;
import static com.evernote.android.job.JobStorage.COLUMN_FLEX_SUPPORT;
import static com.evernote.android.job.JobStorage.COLUMN_ID;
import static com.evernote.android.job.JobStorage.COLUMN_INTERVAL_MS;
import static com.evernote.android.job.JobStorage.COLUMN_LAST_RUN;
import static com.evernote.android.job.JobStorage.COLUMN_NETWORK_TYPE;
import static com.evernote.android.job.JobStorage.COLUMN_NUM_FAILURES;
import static com.evernote.android.job.JobStorage.COLUMN_REQUIREMENTS_ENFORCED;
import static com.evernote.android.job.JobStorage.COLUMN_REQUIRES_BATTERY_NOT_LOW;
import static com.evernote.android.job.JobStorage.COLUMN_REQUIRES_CHARGING;
import static com.evernote.android.job.JobStorage.COLUMN_REQUIRES_DEVICE_IDLE;
import static com.evernote.android.job.JobStorage.COLUMN_REQUIRES_STORAGE_NOT_LOW;
import static com.evernote.android.job.JobStorage.COLUMN_SCHEDULED_AT;
import static com.evernote.android.job.JobStorage.COLUMN_STARTED;
import static com.evernote.android.job.JobStorage.COLUMN_START_MS;
import static com.evernote.android.job.JobStorage.COLUMN_TAG;
import static com.evernote.android.job.JobStorage.COLUMN_TRANSIENT;
import static com.evernote.android.job.JobStorage.DATABASE_NAME;
import static com.evernote.android.job.JobStorage.JOB_TABLE_NAME;
import static org.assertj.core.api.Java6Assertions.assertThat;



/**
 * @author rwondratschek
 */
@RunWith(JobRobolectricTestRunner.class)
@FixMethodOrder(MethodSorters.JVM)
@SuppressWarnings("unused")
public class DatabaseManualUpgradeTest extends BaseJobManagerTest {

    @Test
    public void testDatabaseUpgrade1to6() {
        Context context = ApplicationProvider.getApplicationContext();
        context.deleteDatabase(DATABASE_NAME);

        JobOpenHelper1 openHelper = new JobOpenHelper1(context);
        createDatabase(openHelper, false);
        createJobs(openHelper);

        checkJob();
    }

    @Test
    public void testDatabaseUpgrade2to6() {
        Context context = ApplicationProvider.getApplicationContext();
        context.deleteDatabase(DATABASE_NAME);

        JobOpenHelper2 openHelper = new JobOpenHelper2(context);
        createDatabase(openHelper, false);
        createJobs(openHelper);

        checkJob();
    }

    @Test
    public void testDatabaseUpgrade3to6() {
        Context context = ApplicationProvider.getApplicationContext();
        context.deleteDatabase(DATABASE_NAME);

        JobOpenHelper3 openHelper = new JobOpenHelper3(context);
        createDatabase(openHelper, false);
        createJobs(openHelper, true);

        checkJob();
    }

    @Test
    public void testDatabaseUpgrade4to6() {
        Context context = ApplicationProvider.getApplicationContext();
        context.deleteDatabase(DATABASE_NAME);

        JobOpenHelper4 openHelper = new JobOpenHelper4(context);
        createDatabase(openHelper, false);
        createJobs(openHelper, true);

        checkJob();
    }

    @Test
    public void testDatabaseUpgrade5to6() {
        Context context = ApplicationProvider.getApplicationContext();
        context.deleteDatabase(DATABASE_NAME);

        JobOpenHelper5 openHelper = new JobOpenHelper5(context);
        createDatabase(openHelper, false);
        createJobs(openHelper, true);

        checkJob();
    }

    @Test
    public void testDatabaseUpgrade1to2to3to4to5to6() {
        Context context = ApplicationProvider.getApplicationContext();
        context.deleteDatabase(DATABASE_NAME);

        JobOpenHelper1 openHelper = new JobOpenHelper1(context);
        createDatabase(openHelper, false);
        createJobs(openHelper);

        createDatabase(new JobOpenHelper2(context), true);
        createDatabase(new JobOpenHelper3(context), true);
        createDatabase(new JobOpenHelper4(context), true);
        createDatabase(new JobOpenHelper5(context), true);

        checkJob();
    }

    private void createDatabase(UpgradeAbleJobOpenHelper openHelper, boolean checkUpgraded) {
        SQLiteDatabase database = openHelper.getWritableDatabase();
        assertThat(openHelper.mDatabaseCreated).isTrue();
        if (checkUpgraded) {
            assertThat(openHelper.mDatabaseUpgraded).isTrue();
        }

        database.close();
    }

    private void createJobs(UpgradeAbleJobOpenHelper openHelper) {
        createJobs(openHelper, false);
    }

    private void createJobs(UpgradeAbleJobOpenHelper openHelper, boolean validInterval) {
        SQLiteDatabase database = openHelper.getWritableDatabase();

        ContentValues contentValues = openHelper.createBaseContentValues(1);
        contentValues.put(JobStorage.COLUMN_START_MS, 60_000L);
Answer:         contentValues.put(JobStorage.COLUMN_END_MS, 120_000L);