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 receives a device wakeup event and then passes the work off 30 * to a {@link android.app.Service}, while ensuring that the 31 * device does not 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 * 37 * <h3>Example</h3> 38 * 39 * <p>A {@link WakefulBroadcastReceiver} uses the method 40 * {@link WakefulBroadcastReceiver#startWakefulService startWakefulService()} 41 * to start the service that does the work. This method is comparable to 42 * {@link android.content.Context#startService startService()}, except that 43 * the {@link WakefulBroadcastReceiver} is holding a wake lock when the service 44 * starts. The intent that is passed with 45 * {@link WakefulBroadcastReceiver#startWakefulService startWakefulService()} 46 * holds an extra identifying the wake lock.</p> 47 * 48 * {@sample development/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulReceiver.java complete} 49 * 50 * <p>The service (in this example, an {@link android.app.IntentService}) does 51 * some work. When it is finished, it releases the wake lock by calling 52 * {@link WakefulBroadcastReceiver#completeWakefulIntent 53 * completeWakefulIntent(intent)}. The intent it passes as a parameter 54 * is the same intent that the {@link WakefulBroadcastReceiver} originally 55 * passed in.</p> 56 * 57 * {@sample development/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulService.java complete} 58 */ 59public abstract class WakefulBroadcastReceiver extends BroadcastReceiver { 60 private static final String EXTRA_WAKE_LOCK_ID = "android.support.content.wakelockid"; 61 62 private static final SparseArray<PowerManager.WakeLock> mActiveWakeLocks 63 = new SparseArray<PowerManager.WakeLock>(); 64 private static int mNextId = 1; 65 66 /** 67 * Do a {@link android.content.Context#startService(android.content.Intent) 68 * Context.startService}, but holding a wake lock while the service starts. 69 * This will modify the Intent to hold an extra identifying the wake lock; 70 * when the service receives it in {@link android.app.Service#onStartCommand 71 * Service.onStartCommand}, it should pass back the Intent it receives there to 72 * {@link #completeWakefulIntent(android.content.Intent)} in order to release 73 * the wake lock. 74 * 75 * @param context The Context in which it operate. 76 * @param intent The Intent with which to start the service, as per 77 * {@link android.content.Context#startService(android.content.Intent) 78 * Context.startService}. 79 */ 80 public static ComponentName startWakefulService(Context context, Intent intent) { 81 synchronized (mActiveWakeLocks) { 82 int id = mNextId; 83 mNextId++; 84 if (mNextId <= 0) { 85 mNextId = 1; 86 } 87 88 intent.putExtra(EXTRA_WAKE_LOCK_ID, id); 89 ComponentName comp = context.startService(intent); 90 if (comp == null) { 91 return null; 92 } 93 94 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 95 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 96 "wake:" + comp.flattenToShortString()); 97 wl.setReferenceCounted(false); 98 wl.acquire(60*1000); 99 mActiveWakeLocks.put(id, wl); 100 return comp; 101 } 102 } 103 104 /** 105 * Finish the execution from a previous {@link #startWakefulService}. Any wake lock 106 * that was being held will now be released. 107 * 108 * @param intent The Intent as originally generated by {@link #startWakefulService}. 109 * @return Returns true if the intent is associated with a wake lock that is 110 * now released; returns false if there was no wake lock specified for it. 111 */ 112 public static boolean completeWakefulIntent(Intent intent) { 113 final int id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, 0); 114 if (id == 0) { 115 return false; 116 } 117 synchronized (mActiveWakeLocks) { 118 PowerManager.WakeLock wl = mActiveWakeLocks.get(id); 119 if (wl != null) { 120 wl.release(); 121 mActiveWakeLocks.remove(id); 122 return true; 123 } 124 // We return true whether or not we actually found the wake lock 125 // the return code is defined to indicate whether the Intent contained 126 // an identifier for a wake lock that it was supposed to match. 127 // We just log a warning here if there is no wake lock found, which could 128 // happen for example if this function is called twice on the same 129 // intent or the process is killed and restarted before processing the intent. 130 Log.w("WakefulBroadcastReceiver", "No active wake lock id #" + id); 131 return true; 132 } 133 } 134} 135