• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BackgroundWorkerService()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      */
queueBackgroundWork(final List<Action> actions)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      */
startServiceWithAction(final Action action, final int retryCount)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      */
startServiceWithIntent(final int opcode, final Intent intent)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
onHandleIntent(final Intent intent)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      */
doBackgroundWork(final Action action, final int attempt)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