WakefulBroadcastReceiver.java revision 5831dcb3eeea951273281349285915243bd5a767
1/*
2 * Copyright (C) 2013 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
17package android.support.v4.content;
18
19import android.content.BroadcastReceiver;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.os.PowerManager;
24import android.util.Log;
25import android.util.SparseArray;
26
27/**
28 * Helper for the common pattern of implementing a {@link BroadcastReceiver}
29 * that will receiver a device wakeup event, and wants to pass that work
30 * off to a {@link android.app.Service} while ensuring the device does not
31 * go back to sleep during the transition.
32 *
33 * <p>This class takes care of creating and managing a partial wake lock
34 * for you; you must request the {@link android.Manifest.permission#WAKE_LOCK}
35 * permission to use it.</p>
36 */
37public abstract class WakefulBroadcastReceiver extends BroadcastReceiver {
38    private static final String EXTRA_WAKE_LOCK_ID = "android.support.content.wakelockid";
39
40    private static final SparseArray<PowerManager.WakeLock> mActiveWakeLocks
41            = new SparseArray<PowerManager.WakeLock>();
42    private static int mNextId = 1;
43
44    /**
45     * Do a {@link android.content.Context#startService(android.content.Intent)
46     * Context.startService}, but holding a wake lock while the service starts.
47     * This will modify the Intent to hold an extra identifying the wake lock;
48     * when the service receives it in {@link android.app.Service#onStartCommand
49     * Service.onStartCommand}, it should the Intent it receives there back to
50     * {@link #completeWakefulIntent(android.content.Intent)} in order to release
51     * the wake lock.
52     *
53     * @param context The Context in which it operate.
54     * @param intent The Intent with which to start the service, as per
55     * {@link android.content.Context#startService(android.content.Intent)
56     * Context.startService}.
57     */
58    public static ComponentName startWakefulService(Context context, Intent intent) {
59        synchronized (mActiveWakeLocks) {
60            int id = mNextId;
61            mNextId++;
62            if (mNextId <= 0) {
63                mNextId = 1;
64            }
65
66            intent.putExtra(EXTRA_WAKE_LOCK_ID, id);
67            ComponentName comp = context.startService(intent);
68            if (comp == null) {
69                return null;
70            }
71
72            PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
73            PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
74                    "wake:" + comp.flattenToShortString());
75            wl.setReferenceCounted(false);
76            wl.acquire(60*1000);
77            mActiveWakeLocks.put(id, wl);
78            return comp;
79        }
80    }
81
82    /**
83     * Finish the execution from a previous {@link #startWakefulService}.  Any wake lock
84     * that was being held will now be released.
85     *
86     * @param intent The Intent as originally generated by {@link #startWakefulService}.
87     * @retun Returns true if the intent is associated with a wake lock that is
88     * now released; returns false if there was no wake lock specified for it.
89     */
90    public static boolean completeWakefulIntent(Intent intent) {
91        final int id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, 0);
92        if (id == 0) {
93            return false;
94        }
95        synchronized (mActiveWakeLocks) {
96            PowerManager.WakeLock wl = mActiveWakeLocks.get(id);
97            if (wl != null) {
98                wl.release();
99                mActiveWakeLocks.remove(id);
100                return true;
101            }
102            // We return true whether or not we actually found the wake lock
103            // the return code is defined to indicate whether the Intent contained
104            // an identifier for a wake lock that it was supposed to match.
105            // We just log a warning here if there is no wake lock found, which could
106            // happen for example if this function is called twice on the same
107            // intent or the process is killed and restarted before processing the intent.
108            Log.w("WakefulBroadcastReceiver", "No active wake lock id #" + id);
109            return true;
110        }
111    }
112}
113