Commit b10fbe55 authored by fgorski's avatar fgorski Committed by Commit bot

[Android] Implements OS upgrade check and rescheduling

* Adds an OS upgrade check to the BackgroundTaskScheduler
* Adds OS version tracking to BackgroundTaskSchedulerPrefs
* Ensures GCMNetworkManager based upgrades only happen on supported OSs
* Calls OS version check from DeferredStartupHandler

BUG=710630
R=nyquist@chromium.org

Review-Url: https://codereview.chromium.org/2819703002
Cr-Commit-Position: refs/heads/master@{#473957}
parent 01a6ade0
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.components.background_task_scheduler; package org.chromium.components.background_task_scheduler;
import android.os.Build;
import com.google.android.gms.gcm.GcmNetworkManager; import com.google.android.gms.gcm.GcmNetworkManager;
import com.google.android.gms.gcm.GcmTaskService; import com.google.android.gms.gcm.GcmTaskService;
import com.google.android.gms.gcm.TaskParams; import com.google.android.gms.gcm.TaskParams;
...@@ -125,6 +127,8 @@ public class BackgroundTaskGcmTaskService extends GcmTaskService { ...@@ -125,6 +127,8 @@ public class BackgroundTaskGcmTaskService extends GcmTaskService {
@Override @Override
public void onInitializeTasks() { public void onInitializeTasks() {
// Ignore the event on OSs supporting JobScheduler.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) return;
BackgroundTaskSchedulerFactory.getScheduler().reschedule( BackgroundTaskSchedulerFactory.getScheduler().reschedule(
ContextUtils.getApplicationContext()); ContextUtils.getApplicationContext());
} }
......
...@@ -95,6 +95,41 @@ public class BackgroundTaskScheduler { ...@@ -95,6 +95,41 @@ public class BackgroundTaskScheduler {
mSchedulerDelegate.cancel(context, taskId); mSchedulerDelegate.cancel(context, taskId);
} }
/**
* Checks whether OS was upgraded and triggers rescheduling if it is necessary.
* Rescheduling is necessary if type of background task scheduler delegate is different for a
* new version of the OS.
*
* @param context the current context.
*/
public void checkForOSUpgrade(Context context) {
int oldSdkInt = BackgroundTaskSchedulerPrefs.getLastSdkVersion();
int newSdkInt = Build.VERSION.SDK_INT;
// No OS upgrade detected.
if (oldSdkInt == newSdkInt) return;
// Save the current SDK version to preferences.
BackgroundTaskSchedulerPrefs.setLastSdkVersion(newSdkInt);
// Check for OS upgrades forcing delegate change or "just in case" rescheduling.
if (!osUpgradeChangesDelegateType(oldSdkInt, newSdkInt)) return;
// Explicitly create and invoke old delegate type to cancel all scheduled tasks.
// All preference entries are kept until reschedule call, which removes then then.
BackgroundTaskSchedulerDelegate oldDelegate =
BackgroundTaskSchedulerFactory.getSchedulerDelegateForSdk(oldSdkInt);
Set<Integer> scheduledTaskIds = BackgroundTaskSchedulerPrefs.getScheduledTaskIds();
for (int taskId : scheduledTaskIds) {
oldDelegate.cancel(context, taskId);
}
reschedule(context);
}
/**
* Reschedules all the tasks currently scheduler through BackgroundTaskSheduler.
* @param context the current context.
*/
public void reschedule(Context context) { public void reschedule(Context context) {
Set<String> scheduledTasksClassNames = BackgroundTaskSchedulerPrefs.getScheduledTasks(); Set<String> scheduledTasksClassNames = BackgroundTaskSchedulerPrefs.getScheduledTasks();
BackgroundTaskSchedulerPrefs.removeAllTasks(); BackgroundTaskSchedulerPrefs.removeAllTasks();
...@@ -108,4 +143,8 @@ public class BackgroundTaskScheduler { ...@@ -108,4 +143,8 @@ public class BackgroundTaskScheduler {
task.reschedule(context); task.reschedule(context);
} }
} }
private boolean osUpgradeChangesDelegateType(int oldSdkInt, int newSdkInt) {
return oldSdkInt < Build.VERSION_CODES.M && newSdkInt >= Build.VERSION_CODES.M;
}
} }
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
package org.chromium.components.background_task_scheduler; package org.chromium.components.background_task_scheduler;
import android.annotation.TargetApi;
import android.os.Build; import android.os.Build;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
...@@ -16,9 +15,8 @@ import org.chromium.base.VisibleForTesting; ...@@ -16,9 +15,8 @@ import org.chromium.base.VisibleForTesting;
public final class BackgroundTaskSchedulerFactory { public final class BackgroundTaskSchedulerFactory {
private static BackgroundTaskScheduler sInstance; private static BackgroundTaskScheduler sInstance;
@TargetApi(Build.VERSION_CODES.M) static BackgroundTaskSchedulerDelegate getSchedulerDelegateForSdk(int sdkInt) {
private static BackgroundTaskSchedulerDelegate getSchedulerDelegate() { if (sdkInt >= Build.VERSION_CODES.M) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return new BackgroundTaskSchedulerJobService(); return new BackgroundTaskSchedulerJobService();
} else { } else {
return new BackgroundTaskSchedulerGcmNetworkManager(); return new BackgroundTaskSchedulerGcmNetworkManager();
...@@ -31,7 +29,10 @@ public final class BackgroundTaskSchedulerFactory { ...@@ -31,7 +29,10 @@ public final class BackgroundTaskSchedulerFactory {
*/ */
public static BackgroundTaskScheduler getScheduler() { public static BackgroundTaskScheduler getScheduler() {
ThreadUtils.assertOnUiThread(); ThreadUtils.assertOnUiThread();
if (sInstance == null) sInstance = new BackgroundTaskScheduler(getSchedulerDelegate()); if (sInstance == null) {
sInstance =
new BackgroundTaskScheduler(getSchedulerDelegateForSdk(Build.VERSION.SDK_INT));
}
return sInstance; return sInstance;
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.chromium.components.background_task_scheduler; package org.chromium.components.background_task_scheduler;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
...@@ -17,16 +18,75 @@ import java.util.Set; ...@@ -17,16 +18,75 @@ import java.util.Set;
public class BackgroundTaskSchedulerPrefs { public class BackgroundTaskSchedulerPrefs {
private static final String TAG = "BTSPrefs"; private static final String TAG = "BTSPrefs";
private static final String KEY_SCHEDULED_TASKS = "bts_scheduled_tasks"; private static final String KEY_SCHEDULED_TASKS = "bts_scheduled_tasks";
private static final String KEY_LAST_OS_VERSION = "bts_last_os_version"; private static final String KEY_LAST_SDK_VERSION = "bts_last_sdk_version";
private static final String KEY_LAST_APP_VERSION = "bts_last_app_version";
private static final String ENTRY_SEPARATOR = ":"; /**
* Class abstracting conversions between a string kept in shared preferences and actual values
* held there.
*/
private static class ScheduledTaskPreferenceEntry {
private static final String ENTRY_SEPARATOR = ":";
private String mBackgroundTaskClass;
private int mTaskId;
/** Creates a scheduled task shared preference entry from task info. */
public static ScheduledTaskPreferenceEntry createForTaskInfo(TaskInfo taskInfo) {
return new ScheduledTaskPreferenceEntry(
taskInfo.getBackgroundTaskClass().getName(), taskInfo.getTaskId());
}
/**
* Parses a preference entry from input string.
*
* @param entry An input string to parse.
* @return A parsed entry or null if the input is not valid.
*/
public static ScheduledTaskPreferenceEntry parseEntry(String entry) {
if (entry == null) return null;
String[] entryParts = entry.split(ENTRY_SEPARATOR);
if (entryParts.length != 2 || entryParts[0].isEmpty() || entryParts[1].isEmpty()) {
return null;
}
int taskId = 0;
try {
taskId = Integer.parseInt(entryParts[1]);
} catch (NumberFormatException e) {
return null;
}
return new ScheduledTaskPreferenceEntry(entryParts[0], taskId);
}
public ScheduledTaskPreferenceEntry(String className, int taskId) {
mBackgroundTaskClass = className;
mTaskId = taskId;
}
/**
* Converts a task info to a shared preference entry in the format:
* BACKGROUND_TASK_CLASS_NAME:TASK_ID.
*/
public String toString() {
return mBackgroundTaskClass + ENTRY_SEPARATOR + mTaskId;
}
/** Gets the name of background task class in this entry. */
public String getBackgroundTaskClass() {
return mBackgroundTaskClass;
}
/** Gets the ID of the task in this entry. */
public int getTaskId() {
return mTaskId;
}
}
/** Adds a task to scheduler's preferences, so that it can be rescheduled with OS upgrade. */ /** Adds a task to scheduler's preferences, so that it can be rescheduled with OS upgrade. */
public static void addScheduledTask(TaskInfo taskInfo) { public static void addScheduledTask(TaskInfo taskInfo) {
SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
Set<String> scheduledTasks = Set<String> scheduledTasks =
prefs.getStringSet(KEY_SCHEDULED_TASKS, new HashSet<String>(1)); prefs.getStringSet(KEY_SCHEDULED_TASKS, new HashSet<String>(1));
String prefsEntry = toSharedPreferenceEntry(taskInfo); String prefsEntry = ScheduledTaskPreferenceEntry.createForTaskInfo(taskInfo).toString();
if (scheduledTasks.contains(prefsEntry)) return; if (scheduledTasks.contains(prefsEntry)) return;
// Set returned from getStringSet() should not be modified. // Set returned from getStringSet() should not be modified.
...@@ -38,12 +98,12 @@ public class BackgroundTaskSchedulerPrefs { ...@@ -38,12 +98,12 @@ public class BackgroundTaskSchedulerPrefs {
/** Removes a task from scheduler's preferences. */ /** Removes a task from scheduler's preferences. */
public static void removeScheduledTask(int taskId) { public static void removeScheduledTask(int taskId) {
SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
Set<String> scheduledTasks = prefs.getStringSet(KEY_SCHEDULED_TASKS, new HashSet<String>()); Set<String> scheduledTasks = getScheduledTaskEntries(prefs);
String entryToRemove = null; String entryToRemove = null;
String taskSuffix = ENTRY_SEPARATOR + taskId;
for (String entry : scheduledTasks) { for (String entry : scheduledTasks) {
if (entry.endsWith(taskSuffix)) { ScheduledTaskPreferenceEntry parsed = ScheduledTaskPreferenceEntry.parseEntry(entry);
if (parsed != null && parsed.getTaskId() == taskId) {
entryToRemove = entry; entryToRemove = entry;
break; break;
} }
...@@ -61,36 +121,59 @@ public class BackgroundTaskSchedulerPrefs { ...@@ -61,36 +121,59 @@ public class BackgroundTaskSchedulerPrefs {
/** Gets a set of scheduled task class names. */ /** Gets a set of scheduled task class names. */
public static Set<String> getScheduledTasks() { public static Set<String> getScheduledTasks() {
SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
Set<String> prefsEntries = prefs.getStringSet(KEY_SCHEDULED_TASKS, new HashSet<String>(1)); Set<String> scheduledTask = getScheduledTaskEntries(prefs);
Set<String> scheduledTasksClassNames = new HashSet<>(prefsEntries.size()); Set<String> scheduledTasksClassNames = new HashSet<>(scheduledTask.size());
for (String entry : prefsEntries) { for (String entry : scheduledTask) {
String[] entryParts = entry.split(ENTRY_SEPARATOR); ScheduledTaskPreferenceEntry parsed = ScheduledTaskPreferenceEntry.parseEntry(entry);
if (entryParts.length != 2 || entryParts[0] == null || entryParts[0].isEmpty()) { if (parsed != null) {
continue; scheduledTasksClassNames.add(parsed.getBackgroundTaskClass());
} }
scheduledTasksClassNames.add(entryParts[0]);
} }
return scheduledTasksClassNames; return scheduledTasksClassNames;
} }
/** Gets a set of scheduled task IDs. */
public static Set<Integer> getScheduledTaskIds() {
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
Set<String> scheduledTasks = getScheduledTaskEntries(prefs);
Set<Integer> scheduledTaskIds = new HashSet<>(scheduledTasks.size());
for (String entry : scheduledTasks) {
ScheduledTaskPreferenceEntry parsed = ScheduledTaskPreferenceEntry.parseEntry(entry);
if (parsed != null) {
scheduledTaskIds.add(parsed.getTaskId());
}
}
return scheduledTaskIds;
}
/** Removes all scheduled tasks from shared preferences store. */ /** Removes all scheduled tasks from shared preferences store. */
public static void removeAllTasks() { public static void removeAllTasks() {
ContextUtils.getAppSharedPreferences().edit().remove(KEY_SCHEDULED_TASKS).apply(); ContextUtils.getAppSharedPreferences().edit().remove(KEY_SCHEDULED_TASKS).apply();
} }
/** Gets the last SDK version on which this instance ran. Defaults to current SDK version. */
public static int getLastSdkVersion() {
return ContextUtils.getAppSharedPreferences().getInt(
KEY_LAST_SDK_VERSION, Build.VERSION.SDK_INT);
}
/** Gets the last SDK version on which this instance ran. */
public static void setLastSdkVersion(int sdkVersion) {
ContextUtils.getAppSharedPreferences()
.edit()
.putInt(KEY_LAST_SDK_VERSION, sdkVersion)
.apply();
}
private static void updateScheduledTasks(SharedPreferences prefs, Set<String> tasks) { private static void updateScheduledTasks(SharedPreferences prefs, Set<String> tasks) {
SharedPreferences.Editor editor = prefs.edit(); SharedPreferences.Editor editor = prefs.edit();
editor.putStringSet(KEY_SCHEDULED_TASKS, tasks); editor.putStringSet(KEY_SCHEDULED_TASKS, tasks);
editor.apply(); editor.apply();
} }
/** /** Gets the entries for scheduled tasks from shared preferences. */
* Converts a task info to a shared preference entry in the format: private static Set<String> getScheduledTaskEntries(SharedPreferences prefs) {
* BACKGROUND_TASK_CLASS_NAME:TASK_ID. return prefs.getStringSet(KEY_SCHEDULED_TASKS, new HashSet<String>(1));
* Task ID is necessary to be able to remove the entries from the shared preferences.
*/
private static String toSharedPreferenceEntry(TaskInfo taskInfo) {
return taskInfo.getBackgroundTaskClass().getName() + ENTRY_SEPARATOR + taskInfo.getTaskId();
} }
} }
...@@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals; ...@@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import com.google.android.gms.gcm.GcmNetworkManager; import com.google.android.gms.gcm.GcmNetworkManager;
...@@ -20,6 +21,7 @@ import org.mockito.Mock; ...@@ -20,6 +21,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Feature;
...@@ -121,18 +123,33 @@ public class BackgroundTaskGcmTaskServiceTest { ...@@ -121,18 +123,33 @@ public class BackgroundTaskGcmTaskServiceTest {
@Test @Test
@Feature({"BackgroundTaskScheduler"}) @Feature({"BackgroundTaskScheduler"})
public void testOnInitializeTasks() { public void testOnInitializeTasksOnPreM() {
ReflectionHelpers.setStaticField(
Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP);
TaskInfo task = TaskInfo.createOneOffTask(TaskIds.TEST, TestBackgroundTask.class, TaskInfo task = TaskInfo.createOneOffTask(TaskIds.TEST, TestBackgroundTask.class,
TimeUnit.DAYS.toMillis(1)) TimeUnit.DAYS.toMillis(1))
.build(); .build();
BackgroundTaskSchedulerPrefs.addScheduledTask(task); BackgroundTaskSchedulerPrefs.addScheduledTask(task);
assertEquals(0, TestBackgroundTask.getRescheduleCalls()); assertEquals(0, TestBackgroundTask.getRescheduleCalls());
BackgroundTaskGcmTaskService taskService = new BackgroundTaskGcmTaskService(); new BackgroundTaskGcmTaskService().onInitializeTasks();
taskService.onInitializeTasks();
assertEquals(1, TestBackgroundTask.getRescheduleCalls()); assertEquals(1, TestBackgroundTask.getRescheduleCalls());
} }
@Test
@Feature({"BackgroundTaskScheduler"})
public void testOnInitializeTasksOnMPlus() {
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M);
TaskInfo task = TaskInfo.createOneOffTask(TaskIds.TEST, TestBackgroundTask.class,
TimeUnit.DAYS.toMillis(1))
.build();
BackgroundTaskSchedulerPrefs.addScheduledTask(task);
assertEquals(0, TestBackgroundTask.getRescheduleCalls());
new BackgroundTaskGcmTaskService().onInitializeTasks();
assertEquals(0, TestBackgroundTask.getRescheduleCalls());
}
private TaskParams buildTaskParams(Class clazz, Bundle taskExtras) { private TaskParams buildTaskParams(Class clazz, Bundle taskExtras) {
Bundle extras = new Bundle(); Bundle extras = new Bundle();
extras.putBundle( extras.putBundle(
......
...@@ -8,16 +8,20 @@ import static org.junit.Assert.assertEquals; ...@@ -8,16 +8,20 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import android.os.Build;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Feature;
import org.chromium.testing.local.LocalRobolectricTestRunner; import org.chromium.testing.local.LocalRobolectricTestRunner;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
...@@ -75,6 +79,11 @@ public class BackgroundTaskSchedulerPrefsTest { ...@@ -75,6 +79,11 @@ public class BackgroundTaskSchedulerPrefsTest {
scheduledTasks.contains(TASK_2.getBackgroundTaskClass().getName())); scheduledTasks.contains(TASK_2.getBackgroundTaskClass().getName()));
assertTrue("task3 class name in scheduled tasks.", assertTrue("task3 class name in scheduled tasks.",
scheduledTasks.contains(task3.getBackgroundTaskClass().getName())); scheduledTasks.contains(task3.getBackgroundTaskClass().getName()));
Set<Integer> taskIds = BackgroundTaskSchedulerPrefs.getScheduledTaskIds();
assertTrue(taskIds.contains(TASK_1.getTaskId()));
assertTrue(taskIds.contains(TASK_2.getTaskId()));
assertTrue(taskIds.contains(task3.getTaskId()));
} }
@Test @Test
...@@ -91,10 +100,33 @@ public class BackgroundTaskSchedulerPrefsTest { ...@@ -91,10 +100,33 @@ public class BackgroundTaskSchedulerPrefsTest {
BackgroundTaskSchedulerPrefs.getScheduledTasks().size()); BackgroundTaskSchedulerPrefs.getScheduledTasks().size());
Set<String> scheduledTasks = BackgroundTaskSchedulerPrefs.getScheduledTasks(); Set<String> scheduledTasks = BackgroundTaskSchedulerPrefs.getScheduledTasks();
assertFalse("TASK_1 class name in scheduled tasks.", assertFalse("TASK_1 class name is not in scheduled tasks.",
scheduledTasks.contains(TASK_1.getBackgroundTaskClass().getName())); scheduledTasks.contains(TASK_1.getBackgroundTaskClass().getName()));
assertTrue("TASK_2 class name in scheduled tasks.", assertTrue("TASK_2 class name in scheduled tasks.",
scheduledTasks.contains(TASK_2.getBackgroundTaskClass().getName())); scheduledTasks.contains(TASK_2.getBackgroundTaskClass().getName()));
Set<Integer> taskIds = BackgroundTaskSchedulerPrefs.getScheduledTaskIds();
assertFalse(taskIds.contains(TASK_1.getTaskId()));
assertTrue(taskIds.contains(TASK_2.getTaskId()));
}
@Test
@Feature("BackgroundTaskScheduler")
public void testUnparseableEntries() {
HashSet<String> badEntries = new HashSet<>();
badEntries.add(":123");
badEntries.add("Class:");
badEntries.add("Class:NotAnInt");
badEntries.add("Int field missing");
badEntries.add("Class:123:Too many fields");
badEntries.add("");
badEntries.add(null);
ContextUtils.getAppSharedPreferences()
.edit()
.putStringSet("bts_scheduled_tasks", badEntries)
.apply();
assertTrue(BackgroundTaskSchedulerPrefs.getScheduledTaskIds().isEmpty());
assertTrue(BackgroundTaskSchedulerPrefs.getScheduledTasks().isEmpty());
} }
@Test @Test
...@@ -105,5 +137,19 @@ public class BackgroundTaskSchedulerPrefsTest { ...@@ -105,5 +137,19 @@ public class BackgroundTaskSchedulerPrefsTest {
BackgroundTaskSchedulerPrefs.removeAllTasks(); BackgroundTaskSchedulerPrefs.removeAllTasks();
assertTrue("We are expecting a all tasks to be gone.", assertTrue("We are expecting a all tasks to be gone.",
BackgroundTaskSchedulerPrefs.getScheduledTasks().isEmpty()); BackgroundTaskSchedulerPrefs.getScheduledTasks().isEmpty());
assertTrue("We are expecting a all tasks to be gone.",
BackgroundTaskSchedulerPrefs.getScheduledTaskIds().isEmpty());
}
@Test
@Feature("BackgroundTaskScheduler")
public void testLastSdkVersion() {
ReflectionHelpers.setStaticField(
Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.KITKAT);
assertEquals("Current SDK version should be default.", Build.VERSION_CODES.KITKAT,
BackgroundTaskSchedulerPrefs.getLastSdkVersion());
BackgroundTaskSchedulerPrefs.setLastSdkVersion(Build.VERSION_CODES.LOLLIPOP);
assertEquals(
Build.VERSION_CODES.LOLLIPOP, BackgroundTaskSchedulerPrefs.getLastSdkVersion());
} }
} }
...@@ -4,6 +4,12 @@ ...@@ -4,6 +4,12 @@
package org.chromium.components.background_task_scheduler; package org.chromium.components.background_task_scheduler;
import android.os.Build;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.gcm.GcmNetworkManager;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
...@@ -20,6 +26,10 @@ import org.mockito.Mock; ...@@ -20,6 +26,10 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.internal.ShadowExtractor;
import org.robolectric.shadows.gms.Shadows;
import org.robolectric.shadows.gms.common.ShadowGoogleApiAvailability;
import org.robolectric.util.ReflectionHelpers;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Feature;
...@@ -29,7 +39,8 @@ import java.util.concurrent.TimeUnit; ...@@ -29,7 +39,8 @@ import java.util.concurrent.TimeUnit;
/** Unit tests for {@link BackgroundTaskScheduler}. */ /** Unit tests for {@link BackgroundTaskScheduler}. */
@RunWith(LocalRobolectricTestRunner.class) @RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE) @Config(manifest = Config.NONE,
shadows = {ShadowGcmNetworkManager.class, ShadowGoogleApiAvailability.class})
public class BackgroundTaskSchedulerTest { public class BackgroundTaskSchedulerTest {
private static final TaskInfo TASK = private static final TaskInfo TASK =
TaskInfo.createOneOffTask( TaskInfo.createOneOffTask(
...@@ -38,6 +49,7 @@ public class BackgroundTaskSchedulerTest { ...@@ -38,6 +49,7 @@ public class BackgroundTaskSchedulerTest {
@Mock @Mock
private BackgroundTaskSchedulerDelegate mDelegate; private BackgroundTaskSchedulerDelegate mDelegate;
private ShadowGcmNetworkManager mGcmNetworkManager;
@Before @Before
public void setUp() { public void setUp() {
...@@ -46,6 +58,12 @@ public class BackgroundTaskSchedulerTest { ...@@ -46,6 +58,12 @@ public class BackgroundTaskSchedulerTest {
BackgroundTaskSchedulerFactory.setSchedulerForTesting( BackgroundTaskSchedulerFactory.setSchedulerForTesting(
new BackgroundTaskScheduler(mDelegate)); new BackgroundTaskScheduler(mDelegate));
TestBackgroundTask.reset(); TestBackgroundTask.reset();
// Initialize Google Play Services and GCM Network Manager for upgrade testing.
Shadows.shadowOf(GoogleApiAvailability.getInstance())
.setIsGooglePlayServicesAvailable(ConnectionResult.SUCCESS);
mGcmNetworkManager = (ShadowGcmNetworkManager) ShadowExtractor.extract(
GcmNetworkManager.getInstance(ContextUtils.getApplicationContext()));
} }
@Test @Test
...@@ -95,4 +113,52 @@ public class BackgroundTaskSchedulerTest { ...@@ -95,4 +113,52 @@ public class BackgroundTaskSchedulerTest {
assertEquals(1, TestBackgroundTask.getRescheduleCalls()); assertEquals(1, TestBackgroundTask.getRescheduleCalls());
assertTrue(BackgroundTaskSchedulerPrefs.getScheduledTasks().isEmpty()); assertTrue(BackgroundTaskSchedulerPrefs.getScheduledTasks().isEmpty());
} }
@Test
@Feature({"BackgroundTaskScheduler"})
public void testCheckForOSUpgrade_PreMToMPlus() {
BackgroundTaskSchedulerPrefs.setLastSdkVersion(Build.VERSION_CODES.LOLLIPOP);
BackgroundTaskSchedulerPrefs.addScheduledTask(TASK);
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M);
BackgroundTaskSchedulerFactory.getScheduler().checkForOSUpgrade(
RuntimeEnvironment.application);
assertEquals(Build.VERSION_CODES.M, BackgroundTaskSchedulerPrefs.getLastSdkVersion());
assertTrue(mGcmNetworkManager.getCanceledTaskTags().contains(
Integer.toString(TASK.getTaskId())));
assertEquals(1, TestBackgroundTask.getRescheduleCalls());
}
/** This scenario tests upgrade from pre-M to pre-M OS, which requires no rescheduling. */
@Test
@Feature({"BackgroundTaskScheduler"})
public void testCheckForOSUpgrade_PreMToPreM() {
BackgroundTaskSchedulerPrefs.setLastSdkVersion(Build.VERSION_CODES.KITKAT);
BackgroundTaskSchedulerPrefs.addScheduledTask(TASK);
ReflectionHelpers.setStaticField(
Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP);
BackgroundTaskSchedulerFactory.getScheduler().checkForOSUpgrade(
RuntimeEnvironment.application);
assertEquals(
Build.VERSION_CODES.LOLLIPOP, BackgroundTaskSchedulerPrefs.getLastSdkVersion());
assertEquals(0, TestBackgroundTask.getRescheduleCalls());
}
/** This scenario tests upgrade from M+ to M+ OS, which requires no rescheduling. */
@Test
@Feature({"BackgroundTaskScheduler"})
public void testCheckForOSUpgrade_MPlusToMPlus() {
BackgroundTaskSchedulerPrefs.setLastSdkVersion(Build.VERSION_CODES.M);
BackgroundTaskSchedulerPrefs.addScheduledTask(TASK);
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N);
BackgroundTaskSchedulerFactory.getScheduler().checkForOSUpgrade(
RuntimeEnvironment.application);
assertEquals(Build.VERSION_CODES.N, BackgroundTaskSchedulerPrefs.getLastSdkVersion());
assertEquals(0, TestBackgroundTask.getRescheduleCalls());
}
} }
...@@ -11,6 +11,9 @@ import com.google.android.gms.gcm.Task; ...@@ -11,6 +11,9 @@ import com.google.android.gms.gcm.Task;
import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements; import org.robolectric.annotation.Implements;
import java.util.HashSet;
import java.util.Set;
/** /**
* Custom shadow for the OS's GcmNetworkManager. We use this to hook the call to GcmNetworkManager * Custom shadow for the OS's GcmNetworkManager. We use this to hook the call to GcmNetworkManager
* to make sure it was invoked as we expect. * to make sure it was invoked as we expect.
...@@ -19,6 +22,11 @@ import org.robolectric.annotation.Implements; ...@@ -19,6 +22,11 @@ import org.robolectric.annotation.Implements;
public class ShadowGcmNetworkManager { public class ShadowGcmNetworkManager {
private Task mTask; private Task mTask;
private Task mCanceledTask; private Task mCanceledTask;
private Set<String> mCanceledTaskTags;
public ShadowGcmNetworkManager() {
mCanceledTaskTags = new HashSet<>();
}
@Implementation @Implementation
public void schedule(Task task) { public void schedule(Task task) {
...@@ -33,6 +41,7 @@ public class ShadowGcmNetworkManager { ...@@ -33,6 +41,7 @@ public class ShadowGcmNetworkManager {
mCanceledTask = mTask; mCanceledTask = mTask;
mTask = null; mTask = null;
} }
mCanceledTaskTags.add(tag);
} }
public Task getScheduledTask() { public Task getScheduledTask() {
...@@ -43,8 +52,13 @@ public class ShadowGcmNetworkManager { ...@@ -43,8 +52,13 @@ public class ShadowGcmNetworkManager {
return mCanceledTask; return mCanceledTask;
} }
public Set<String> getCanceledTaskTags() {
return mCanceledTaskTags;
}
public void clear() { public void clear() {
mTask = null; mTask = null;
mCanceledTask = null; mCanceledTask = null;
mCanceledTaskTags = new HashSet<>();
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment