blob: 4d4b1506bd77558b92da981b53fdfcd16a58db82 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.datamodel.action;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.android.messaging.Factory;
import com.android.messaging.datamodel.DataModel;
import com.android.messaging.datamodel.DataModelException;
import com.android.messaging.util.Assert;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.LoggingTimer;
import com.android.messaging.util.WakeLockHelper;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
/**
* Background worker service is an initial example of a background work queue handler
* Used to actually "send" messages which may take some time and should not block ActionService
* or UI
*/
public class BackgroundWorkerService extends IntentService {
private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
private static final boolean VERBOSE = false;
private static final String WAKELOCK_ID = "bugle_background_worker_wakelock";
@VisibleForTesting
static WakeLockHelper sWakeLock = new WakeLockHelper(WAKELOCK_ID);
private final ActionService mHost;
public BackgroundWorkerService() {
super("BackgroundWorker");
mHost = DataModel.get().getActionService();
}
/**
* Queue a list of requests from action service to this worker
*/
public static void queueBackgroundWork(final List<Action> actions) {
for (final Action action : actions) {
startServiceWithAction(action, 0);
}
}
// ops
@VisibleForTesting
protected static final int OP_PROCESS_REQUEST = 400;
// extras
@VisibleForTesting
protected static final String EXTRA_OP_CODE = "op";
@VisibleForTesting
protected static final String EXTRA_ACTION = "action";
@VisibleForTesting
protected static final String EXTRA_ATTEMPT = "retry_attempt";
/**
* Queue action intent to the BackgroundWorkerService after acquiring wake lock
*/
private static void startServiceWithAction(final Action action,
final int retryCount) {
final Intent intent = new Intent();
intent.putExtra(EXTRA_ACTION, action);
intent.putExtra(EXTRA_ATTEMPT, retryCount);
startServiceWithIntent(OP_PROCESS_REQUEST, intent);
}
/**
* Queue intent to the BackgroundWorkerService after acquiring wake lock
*/
private static void startServiceWithIntent(final int opcode, final Intent intent) {
final Context context = Factory.get().getApplicationContext();
intent.setClass(context, BackgroundWorkerService.class);
intent.putExtra(EXTRA_OP_CODE, opcode);
sWakeLock.acquire(context, intent, opcode);
if (VERBOSE) {
LogUtil.v(TAG, "acquiring wakelock for opcode " + opcode);
}
if (context.startService(intent) == null) {
LogUtil.e(TAG,
"BackgroundWorkerService.startServiceWithAction: failed to start service for "
+ opcode);
sWakeLock.release(intent, opcode);
}
}
@Override
protected void onHandleIntent(final Intent intent) {
if (intent == null) {
// Shouldn't happen but sometimes does following another crash.
LogUtil.w(TAG, "BackgroundWorkerService.onHandleIntent: Called with null intent");
return;
}
final int opcode = intent.getIntExtra(EXTRA_OP_CODE, 0);
sWakeLock.ensure(intent, opcode);
try {
switch(opcode) {
case OP_PROCESS_REQUEST: {
final Action action = intent.getParcelableExtra(EXTRA_ACTION);
final int attempt = intent.getIntExtra(EXTRA_ATTEMPT, -1);
doBackgroundWork(action, attempt);
break;
}
default:
throw new RuntimeException("Unrecognized opcode in BackgroundWorkerService");
}
} finally {
sWakeLock.release(intent, opcode);
}
}
/**
* Local execution of background work for action on ActionService thread
*/
private void doBackgroundWork(final Action action, final int attempt) {
action.markBackgroundWorkStarting();
Bundle response = null;
try {
final LoggingTimer timer = new LoggingTimer(
TAG, action.getClass().getSimpleName() + "#doBackgroundWork");
timer.start();
response = action.doBackgroundWork();
timer.stopAndLog();
action.markBackgroundCompletionQueued();
mHost.handleResponseFromBackgroundWorker(action, response);
} catch (final Exception exception) {
final boolean retry = false;
LogUtil.e(TAG, "Error in background worker", exception);
if (!(exception instanceof DataModelException)) {
// DataModelException is expected (sort-of) and handled in handleFailureFromWorker
// below, but other exceptions should crash ENG builds
Assert.fail("Unexpected error in background worker - abort");
}
if (retry) {
action.markBackgroundWorkQueued();
startServiceWithAction(action, attempt + 1);
} else {
action.markBackgroundCompletionQueued();
mHost.handleFailureFromBackgroundWorker(action, exception);
}
}
}
}