// Copyright 2016 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.chrome.browser.offlinepages;
import android.content.Context;
import android.os.Bundle;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.Callback;
import org.chromium.base.Log;
import org.chromium.base.SysUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.ChromeBackgroundServiceWaiter;
import org.chromium.chrome.browser.offlinepages.interfaces.BackgroundSchedulerProcessor;
/**
* Handles servicing of background offlining requests coming via the GcmNetworkManager.
*/
public class BackgroundOfflinerTask {
private static final String TAG = "BGOfflinerTask";
private static final long DEFER_START_SECONDS = 5 * 60;
public BackgroundOfflinerTask(BackgroundSchedulerProcessor bridge) {
mBridge = bridge;
}
private final BackgroundSchedulerProcessor mBridge;
/**
* Triggers processing of background offlining requests. This is called when
* system conditions are appropriate for background offlining, typically from the
* GcmTaskService onRunTask() method. In response, we will start the
* task processing by passing the call along to the C++ RequestCoordinator.
* Also starts UMA collection.
*
* @returns true for success
*/
public boolean startBackgroundRequests(Context context, Bundle bundle,
ChromeBackgroundServiceWaiter waiter) {
// Set up backup scheduled task in case processing is killed before RequestCoordinator
// has a chance to reschedule base on remaining work.
TriggerConditions previousTriggerConditions =
TaskExtrasPacker.unpackTriggerConditionsFromBundle(bundle);
BackgroundScheduler.backupSchedule(context, previousTriggerConditions, DEFER_START_SECONDS);
DeviceConditions currentConditions = OfflinePageUtils.getDeviceConditions(context);
if (!currentConditions.isPowerConnected()
&& currentConditions.getBatteryPercentage()
< previousTriggerConditions.getMinimumBatteryPercentage()) {
Log.d(TAG, "Battery percentage is lower than minimum to start processing");
return false;
}
if (SysUtils.isLowEndDevice() && ApplicationStatus.hasVisibleActivities()) {
Log.d(TAG, "Application visible on low-end device so deferring background processing");
return false;
}
// Now initiate processing.
processBackgroundRequests(bundle, currentConditions, waiter);
// Gather UMA data to measure how often the user's machine is amenable to background
// loading when we wake to do a task.
long taskScheduledTimeMillis = TaskExtrasPacker.unpackTimeFromBundle(bundle);
OfflinePageUtils.recordWakeupUMA(context, taskScheduledTimeMillis);
return true;
}
/**
* Triggers processing of background offlining requests.
*/
// TODO(petewil): Change back to private when UMA works in the test, and test
// startBackgroundRequests instead of this method.
@VisibleForTesting
public void processBackgroundRequests(
Bundle bundle, DeviceConditions deviceConditions,
final ChromeBackgroundServiceWaiter waiter) {
// TODO(petewil): Nothing is holding the Wake Lock. We need some solution to
// keep hold of it. Options discussed so far are having a fresh set of functions
// to grab and release a countdown latch, or holding onto the wake lock ourselves,
// or grabbing the wake lock and then starting chrome and running startProcessing
// on the UI thread.
// TODO(petewil): Decode the TriggerConditions from the bundle.
Callback<Boolean> callback = new Callback<Boolean>() {
/**
* Callback function which indicates completion of background work.
* @param result - true if work was actually done (used for UMA).
*/
@Override
public void onResult(Boolean result) {
// Release the wake lock.
Log.d(TAG, "onProcessingDone");
waiter.onWaitDone();
}
};
// Pass the activation on to the bridge to the C++ RequestCoordinator.
if (!mBridge.startProcessing(deviceConditions, callback)) {
// Processing not started currently. Let callback know.
callback.onResult(false);
}
}
}