Home | History | Annotate | Download | only in action
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.messaging.datamodel.action;
     18 
     19 import android.app.IntentService;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.os.Bundle;
     23 
     24 import com.android.messaging.Factory;
     25 import com.android.messaging.datamodel.DataModel;
     26 import com.android.messaging.datamodel.DataModelException;
     27 import com.android.messaging.util.Assert;
     28 import com.android.messaging.util.LogUtil;
     29 import com.android.messaging.util.LoggingTimer;
     30 import com.android.messaging.util.WakeLockHelper;
     31 import com.google.common.annotations.VisibleForTesting;
     32 
     33 import java.util.List;
     34 
     35 /**
     36  * Background worker service is an initial example of a background work queue handler
     37  * Used to actually "send" messages which may take some time and should not block ActionService
     38  * or UI
     39  */
     40 public class BackgroundWorkerService extends IntentService {
     41     private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
     42     private static final boolean VERBOSE = false;
     43 
     44     private static final String WAKELOCK_ID = "bugle_background_worker_wakelock";
     45     @VisibleForTesting
     46     static WakeLockHelper sWakeLock = new WakeLockHelper(WAKELOCK_ID);
     47 
     48     private final ActionService mHost;
     49 
     50     public BackgroundWorkerService() {
     51         super("BackgroundWorker");
     52         mHost = DataModel.get().getActionService();
     53     }
     54 
     55     /**
     56      * Queue a list of requests from action service to this worker
     57      */
     58     public static void queueBackgroundWork(final List<Action> actions) {
     59         for (final Action action : actions) {
     60             startServiceWithAction(action, 0);
     61         }
     62     }
     63 
     64     // ops
     65     @VisibleForTesting
     66     protected static final int OP_PROCESS_REQUEST = 400;
     67 
     68     // extras
     69     @VisibleForTesting
     70     protected static final String EXTRA_OP_CODE = "op";
     71     @VisibleForTesting
     72     protected static final String EXTRA_ACTION = "action";
     73     @VisibleForTesting
     74     protected static final String EXTRA_ATTEMPT = "retry_attempt";
     75 
     76     /**
     77      * Queue action intent to the BackgroundWorkerService after acquiring wake lock
     78      */
     79     private static void startServiceWithAction(final Action action,
     80             final int retryCount) {
     81         final Intent intent = new Intent();
     82         intent.putExtra(EXTRA_ACTION, action);
     83         intent.putExtra(EXTRA_ATTEMPT, retryCount);
     84         startServiceWithIntent(OP_PROCESS_REQUEST, intent);
     85     }
     86 
     87     /**
     88      * Queue intent to the BackgroundWorkerService after acquiring wake lock
     89      */
     90     private static void startServiceWithIntent(final int opcode, final Intent intent) {
     91         final Context context = Factory.get().getApplicationContext();
     92 
     93         intent.setClass(context, BackgroundWorkerService.class);
     94         intent.putExtra(EXTRA_OP_CODE, opcode);
     95         sWakeLock.acquire(context, intent, opcode);
     96         if (VERBOSE) {
     97             LogUtil.v(TAG, "acquiring wakelock for opcode " + opcode);
     98         }
     99 
    100         if (context.startService(intent) == null) {
    101             LogUtil.e(TAG,
    102                     "BackgroundWorkerService.startServiceWithAction: failed to start service for "
    103                     + opcode);
    104             sWakeLock.release(intent, opcode);
    105         }
    106     }
    107 
    108     @Override
    109     protected void onHandleIntent(final Intent intent) {
    110         if (intent == null) {
    111             // Shouldn't happen but sometimes does following another crash.
    112             LogUtil.w(TAG, "BackgroundWorkerService.onHandleIntent: Called with null intent");
    113             return;
    114         }
    115         final int opcode = intent.getIntExtra(EXTRA_OP_CODE, 0);
    116         sWakeLock.ensure(intent, opcode);
    117 
    118         try {
    119             switch(opcode) {
    120                 case OP_PROCESS_REQUEST: {
    121                     final Action action = intent.getParcelableExtra(EXTRA_ACTION);
    122                     final int attempt = intent.getIntExtra(EXTRA_ATTEMPT, -1);
    123                     doBackgroundWork(action, attempt);
    124                     break;
    125                 }
    126 
    127                 default:
    128                     throw new RuntimeException("Unrecognized opcode in BackgroundWorkerService");
    129             }
    130         } finally {
    131             sWakeLock.release(intent, opcode);
    132         }
    133     }
    134 
    135     /**
    136      * Local execution of background work for action on ActionService thread
    137      */
    138     private void doBackgroundWork(final Action action, final int attempt) {
    139         action.markBackgroundWorkStarting();
    140         Bundle response = null;
    141         try {
    142             final LoggingTimer timer = new LoggingTimer(
    143                     TAG, action.getClass().getSimpleName() + "#doBackgroundWork");
    144             timer.start();
    145 
    146             response = action.doBackgroundWork();
    147 
    148             timer.stopAndLog();
    149             action.markBackgroundCompletionQueued();
    150             mHost.handleResponseFromBackgroundWorker(action, response);
    151         } catch (final Exception exception) {
    152             final boolean retry = false;
    153             LogUtil.e(TAG, "Error in background worker", exception);
    154             if (!(exception instanceof DataModelException)) {
    155                 // DataModelException is expected (sort-of) and handled in handleFailureFromWorker
    156                 // below, but other exceptions should crash ENG builds
    157                 Assert.fail("Unexpected error in background worker - abort");
    158             }
    159             if (retry) {
    160                 action.markBackgroundWorkQueued();
    161                 startServiceWithAction(action, attempt + 1);
    162             } else {
    163                 action.markBackgroundCompletionQueued();
    164                 mHost.handleFailureFromBackgroundWorker(action, exception);
    165             }
    166         }
    167     }
    168 }
    169