1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/*
2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project
3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License");
5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License.
6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at
7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *      http://www.apache.org/licenses/LICENSE-2.0
9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software
11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS,
12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and
14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License.
15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.datamodel.action;
18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.AlarmManager;
20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.IntentService;
21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.PendingIntent;
22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.BroadcastReceiver;
23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context;
24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Intent;
25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Bundle;
26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.SystemClock;
27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.Factory;
29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.DataModel;
30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil;
31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LoggingTimer;
32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.WakeLockHelper;
33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.google.common.annotations.VisibleForTesting;
34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/**
36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * ActionService used to perform background processing for data model
37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class ActionServiceImpl extends IntentService {
39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final boolean VERBOSE = false;
41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public ActionServiceImpl() {
43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        super("ActionService");
44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Start action by sending intent to the service
48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param action - action to start
49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static void startAction(final Action action) {
51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Intent intent = makeIntent(OP_START_ACTION);
52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Bundle actionBundle = new Bundle();
53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        actionBundle.putParcelable(BUNDLE_ACTION, action);
54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        intent.putExtra(EXTRA_ACTION_BUNDLE, actionBundle);
55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        action.markStart();
56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        startServiceWithIntent(intent);
57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Schedule an action to run after specified delay using alarm manager to send pendingintent
61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param action - action to start
62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param requestCode - request code used to collapse requests
63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param delayMs - delay in ms (from now) before action will start
64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static void scheduleAction(final Action action, final int requestCode,
66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final long delayMs) {
67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Intent intent = PendingActionReceiver.makeIntent(OP_START_ACTION);
68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Bundle actionBundle = new Bundle();
69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        actionBundle.putParcelable(BUNDLE_ACTION, action);
70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        intent.putExtra(EXTRA_ACTION_BUNDLE, actionBundle);
71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        PendingActionReceiver.scheduleAlarm(intent, requestCode, delayMs);
73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Handle response returned by BackgroundWorker
77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param request - request generating response
78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param response - response from service
79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static void handleResponseFromBackgroundWorker(final Action action,
81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Bundle response) {
82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Intent intent = makeIntent(OP_RECEIVE_BACKGROUND_RESPONSE);
83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Bundle actionBundle = new Bundle();
85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        actionBundle.putParcelable(BUNDLE_ACTION, action);
86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        intent.putExtra(EXTRA_ACTION_BUNDLE, actionBundle);
87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        intent.putExtra(EXTRA_WORKER_RESPONSE, response);
88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        startServiceWithIntent(intent);
90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Handle response returned by BackgroundWorker
94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param request - request generating failure
95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static void handleFailureFromBackgroundWorker(final Action action,
97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Exception exception) {
98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Intent intent = makeIntent(OP_RECEIVE_BACKGROUND_FAILURE);
99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Bundle actionBundle = new Bundle();
101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        actionBundle.putParcelable(BUNDLE_ACTION, action);
102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        intent.putExtra(EXTRA_ACTION_BUNDLE, actionBundle);
103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        intent.putExtra(EXTRA_WORKER_EXCEPTION, exception);
104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        startServiceWithIntent(intent);
106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // ops
109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int OP_START_ACTION = 200;
111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int OP_RECEIVE_BACKGROUND_RESPONSE = 201;
113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int OP_RECEIVE_BACKGROUND_FAILURE = 202;
115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // extras
117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final String EXTRA_OP_CODE = "op";
119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final String EXTRA_ACTION_BUNDLE = "datamodel_action_bundle";
121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final String EXTRA_WORKER_EXCEPTION = "worker_exception";
123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final String EXTRA_WORKER_RESPONSE = "worker_response";
125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final String EXTRA_WORKER_UPDATE = "worker_update";
127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final String BUNDLE_ACTION = "bundle_action";
129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private BackgroundWorker mBackgroundWorker;
131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Allocate an intent with a specific opcode.
134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static Intent makeIntent(final int opcode) {
136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Intent intent = new Intent(Factory.get().getApplicationContext(),
137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                ActionServiceImpl.class);
138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        intent.putExtra(EXTRA_OP_CODE, opcode);
139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return intent;
140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Broadcast receiver for alarms scheduled through ActionService.
144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static class PendingActionReceiver extends BroadcastReceiver {
146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        static final String ACTION = "com.android.messaging.datamodel.PENDING_ACTION";
147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Allocate an intent with a specific opcode and alarm action.
150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public static Intent makeIntent(final int opcode) {
152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Intent intent = new Intent(Factory.get().getApplicationContext(),
153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    PendingActionReceiver.class);
154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            intent.setAction(ACTION);
155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            intent.putExtra(EXTRA_OP_CODE, opcode);
156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return intent;
157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public static void scheduleAlarm(final Intent intent, final int requestCode,
160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final long delayMs) {
161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Context context = Factory.get().getApplicationContext();
162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final PendingIntent pendingIntent = PendingIntent.getBroadcast(
163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    context, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final AlarmManager mgr =
166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (delayMs < Long.MAX_VALUE) {
169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                mgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        SystemClock.elapsedRealtime() + delayMs, pendingIntent);
171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } else {
172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                mgr.cancel(pendingIntent);
173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * {@inheritDoc}
178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public void onReceive(final Context context, final Intent intent) {
181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            ActionServiceImpl.startServiceWithIntent(intent);
182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Creates a pending intent that will trigger a data model action when the intent is
187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * triggered
188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static PendingIntent makeStartActionPendingIntent(final Context context,
190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Action action, final int requestCode, final boolean launchesAnActivity) {
191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Intent intent = PendingActionReceiver.makeIntent(OP_START_ACTION);
192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Bundle actionBundle = new Bundle();
193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        actionBundle.putParcelable(BUNDLE_ACTION, action);
194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        intent.putExtra(EXTRA_ACTION_BUNDLE, actionBundle);
195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (launchesAnActivity) {
196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return PendingIntent.getBroadcast(context, requestCode, intent,
199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                PendingIntent.FLAG_UPDATE_CURRENT);
200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * {@inheritDoc}
204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void onCreate() {
207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        super.onCreate();
208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mBackgroundWorker = DataModel.get().getBackgroundWorkerForActionService();
209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        DataModel.get().getConnectivityUtil().registerForSignalStrength();
210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void onDestroy() {
214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        super.onDestroy();
215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        DataModel.get().getConnectivityUtil().unregisterForSignalStrength();
216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String WAKELOCK_ID = "bugle_datamodel_service_wakelock";
219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    static WakeLockHelper sWakeLock = new WakeLockHelper(WAKELOCK_ID);
221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Queue intent to the ActionService after acquiring wake lock
224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static void startServiceWithIntent(final Intent intent) {
226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Context context = Factory.get().getApplicationContext();
227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int opcode = intent.getIntExtra(EXTRA_OP_CODE, 0);
228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Increase refCount on wake lock - acquiring if necessary
229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (VERBOSE) {
230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.v(TAG, "acquiring wakelock for opcode " + opcode);
231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        sWakeLock.acquire(context, intent, opcode);
233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        intent.setClass(context, ActionServiceImpl.class);
234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // TODO: Note that intent will be quietly discarded if it exceeds available rpc
236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // memory (in total around 1MB). See this article for background
237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // http://developer.android.com/reference/android/os/TransactionTooLargeException.html
238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Perhaps we should keep large structures in the action monitor?
239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (context.startService(intent) == null) {
240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(TAG,
241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    "ActionService.startServiceWithIntent: failed to start service for intent "
242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    + intent);
243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            sWakeLock.release(intent, opcode);
244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * {@inheritDoc}
249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected void onHandleIntent(final Intent intent) {
252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (intent == null) {
253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // Shouldn't happen but sometimes does following another crash.
254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(TAG, "ActionService.onHandleIntent: Called with null intent");
255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int opcode = intent.getIntExtra(EXTRA_OP_CODE, 0);
258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        sWakeLock.ensure(intent, opcode);
259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Action action;
262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Bundle actionBundle = intent.getBundleExtra(EXTRA_ACTION_BUNDLE);
263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            actionBundle.setClassLoader(getClassLoader());
264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            switch(opcode) {
265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                case OP_START_ACTION: {
266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    action = (Action) actionBundle.getParcelable(BUNDLE_ACTION);
267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    executeAction(action);
268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    break;
269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                case OP_RECEIVE_BACKGROUND_RESPONSE: {
272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    action = (Action) actionBundle.getParcelable(BUNDLE_ACTION);
273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    final Bundle response = intent.getBundleExtra(EXTRA_WORKER_RESPONSE);
274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    processBackgroundResponse(action, response);
275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    break;
276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                case OP_RECEIVE_BACKGROUND_FAILURE: {
279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    action = (Action) actionBundle.getParcelable(BUNDLE_ACTION);
280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    processBackgroundFailure(action);
281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    break;
282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                default:
285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    throw new RuntimeException("Unrecognized opcode in ActionServiceImpl");
286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            action.sendBackgroundActions(mBackgroundWorker);
289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } finally {
290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // Decrease refCount on wake lock - releasing if necessary
291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            sWakeLock.release(intent, opcode);
292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final long EXECUTION_TIME_WARN_LIMIT_MS = 1000; // 1 second
296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Local execution of action on ActionService thread
298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void executeAction(final Action action) {
300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        action.markBeginExecute();
301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final LoggingTimer timer = createLoggingTimer(action, "#executeAction");
303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        timer.start();
304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Object result = action.executeAction();
306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        timer.stopAndLog();
308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        action.markEndExecute(result);
310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Process response on ActionService thread
314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void processBackgroundResponse(final Action action, final Bundle response) {
316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final LoggingTimer timer = createLoggingTimer(action, "#processBackgroundResponse");
317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        timer.start();
318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        action.processBackgroundWorkResponse(response);
320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        timer.stopAndLog();
322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Process failure on ActionService thread
326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void processBackgroundFailure(final Action action) {
328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final LoggingTimer timer = createLoggingTimer(action, "#processBackgroundFailure");
329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        timer.start();
330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        action.processBackgroundWorkFailure();
332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        timer.stopAndLog();
334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static LoggingTimer createLoggingTimer(
337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Action action, final String methodName) {
338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return new LoggingTimer(TAG, action.getClass().getSimpleName() + methodName,
339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                EXECUTION_TIME_WARN_LIMIT_MS);
340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd}
342