blob: bc759eb8e45fc81831e4d116527b3dc979aad3ae [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.background_task_scheduler;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.gcm.GcmNetworkManager;
import com.google.android.gms.gcm.OneoffTask;
import com.google.android.gms.gcm.PeriodicTask;
import com.google.android.gms.gcm.Task;
import com.google.android.gms.gcm.TaskParams;
import org.chromium.base.Log;
import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting;
import java.util.concurrent.TimeUnit;
/**
* An implementation of {@link BackgroundTaskSchedulerDelegate} that uses the Play Services
* {@link GcmNetworkManager} to schedule jobs.
*/
class BackgroundTaskSchedulerGcmNetworkManager implements BackgroundTaskSchedulerDelegate {
private static final String TAG = "BkgrdTaskSchedGcmNM";
@VisibleForTesting
static final String BACKGROUND_TASK_CLASS_KEY = "_background_task_class";
@VisibleForTesting
static final String BACKGROUND_TASK_EXTRAS_KEY = "_background_task_extras";
static BackgroundTask getBackgroundTaskFromTaskParams(@NonNull TaskParams taskParams) {
String backgroundTaskClassName = getBackgroundTaskClassFromTaskParams(taskParams);
return BackgroundTaskReflection.getBackgroundTaskFromClassName(backgroundTaskClassName);
}
private static String getBackgroundTaskClassFromTaskParams(@NonNull TaskParams taskParams) {
Bundle extras = taskParams.getExtras();
if (extras == null) return null;
return extras.getString(BACKGROUND_TASK_CLASS_KEY);
}
/**
* Retrieves the {@link TaskParameters} from the {@link TaskParams}, which are passed as
* one of the keys. Only values valid for {@link android.os.BaseBundle} are supported, and other
* values are stripped at the time when the task is scheduled.
*
* @param taskParams the {@link TaskParams} to extract the {@link TaskParameters} from.
* @return the {@link TaskParameters} for the current job.
*/
static TaskParameters getTaskParametersFromTaskParams(@NonNull TaskParams taskParams) {
int taskId;
try {
taskId = Integer.parseInt(taskParams.getTag());
} catch (NumberFormatException e) {
Log.e(TAG, "Cound not parse task ID from task tag: " + taskParams.getTag());
return null;
}
TaskParameters.Builder builder = TaskParameters.create(taskId);
Bundle extras = taskParams.getExtras();
Bundle taskExtras = extras.getBundle(BACKGROUND_TASK_EXTRAS_KEY);
builder.addExtras(taskExtras);
return builder.build();
}
@VisibleForTesting
static Task createTaskFromTaskInfo(@NonNull TaskInfo taskInfo) {
Bundle taskExtras = new Bundle();
taskExtras.putString(
BACKGROUND_TASK_CLASS_KEY, taskInfo.getBackgroundTaskClass().getName());
taskExtras.putBundle(BACKGROUND_TASK_EXTRAS_KEY, taskInfo.getExtras());
Task.Builder builder;
if (taskInfo.isPeriodic()) {
builder = getPeriodicTaskBuilder(taskInfo.getPeriodicInfo());
} else {
builder = getOneOffTaskBuilder(taskInfo.getOneOffInfo());
}
builder.setExtras(taskExtras)
.setPersisted(taskInfo.isPersisted())
.setRequiredNetwork(getGcmNetworkManagerNetworkTypeFromTypeFromTaskNetworkType(
taskInfo.getRequiredNetworkType()))
.setRequiresCharging(taskInfo.requiresCharging())
.setService(BackgroundTaskGcmTaskService.class)
.setTag(taskIdToTaskTag(taskInfo.getTaskId()))
.setUpdateCurrent(taskInfo.shouldUpdateCurrent());
return builder.build();
}
private static Task.Builder getPeriodicTaskBuilder(TaskInfo.PeriodicInfo periodicInfo) {
PeriodicTask.Builder builder = new PeriodicTask.Builder();
builder.setPeriod(TimeUnit.MILLISECONDS.toSeconds(periodicInfo.getIntervalMs()));
if (periodicInfo.hasFlex()) {
builder.setFlex(TimeUnit.MILLISECONDS.toSeconds(periodicInfo.getFlexMs()));
}
return builder;
}
private static Task.Builder getOneOffTaskBuilder(TaskInfo.OneOffInfo oneOffInfo) {
OneoffTask.Builder builder = new OneoffTask.Builder();
long windowStartSeconds = oneOffInfo.hasWindowStartTimeConstraint()
? TimeUnit.MILLISECONDS.toSeconds(oneOffInfo.getWindowStartTimeMs())
: 0;
builder.setExecutionWindow(windowStartSeconds,
TimeUnit.MILLISECONDS.toSeconds(oneOffInfo.getWindowEndTimeMs()));
return builder;
}
private static int getGcmNetworkManagerNetworkTypeFromTypeFromTaskNetworkType(
@TaskInfo.NetworkType int networkType) {
switch (networkType) {
// This is correct: GcmNM ANY means no network is guaranteed.
case TaskInfo.NetworkType.NONE:
return Task.NETWORK_STATE_ANY;
case TaskInfo.NetworkType.ANY:
return Task.NETWORK_STATE_CONNECTED;
case TaskInfo.NetworkType.UNMETERED:
return Task.NETWORK_STATE_UNMETERED;
default:
assert false;
}
return Task.NETWORK_STATE_ANY;
}
@Override
public boolean schedule(Context context, @NonNull TaskInfo taskInfo) {
ThreadUtils.assertOnUiThread();
if (!BackgroundTaskReflection.hasParameterlessPublicConstructor(
taskInfo.getBackgroundTaskClass())) {
Log.e(TAG,
"BackgroundTask " + taskInfo.getBackgroundTaskClass()
+ " has no parameterless public constructor.");
return false;
}
GcmNetworkManager gcmNetworkManager = getGcmNetworkManager(context);
if (gcmNetworkManager == null) {
Log.e(TAG, "GcmNetworkManager is not available.");
return false;
}
try {
Task task = createTaskFromTaskInfo(taskInfo);
gcmNetworkManager.schedule(task);
} catch (IllegalArgumentException e) {
String gcmErrorMessage = e.getMessage() == null ? "null." : e.getMessage();
Log.e(TAG,
"GcmNetworkManager failed to schedule task, gcm message: " + gcmErrorMessage);
return false;
}
return true;
}
@Override
public void cancel(Context context, int taskId) {
ThreadUtils.assertOnUiThread();
GcmNetworkManager gcmNetworkManager = getGcmNetworkManager(context);
if (gcmNetworkManager == null) {
Log.e(TAG, "GcmNetworkManager is not available.");
return;
}
try {
gcmNetworkManager.cancelTask(
taskIdToTaskTag(taskId), BackgroundTaskGcmTaskService.class);
} catch (IllegalArgumentException e) {
Log.e(TAG, "GcmNetworkManager failed to cancel task.");
}
}
private GcmNetworkManager getGcmNetworkManager(Context context) {
if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
== ConnectionResult.SUCCESS) {
return GcmNetworkManager.getInstance(context);
}
return null;
}
private static String taskIdToTaskTag(int taskId) {
return Integer.toString(taskId);
}
}