1 /* 2 * Copyright 2018 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 androidx.legacy.content; 18 19 import android.content.BroadcastReceiver; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.os.PowerManager; 24 import android.util.Log; 25 import android.util.SparseArray; 26 27 /** 28 * This helper is for an old 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 * <p>Wakelocks held by this class are reported to tools as 38 * {@code "androidx.core:wake:<component-name>"}.</p> 39 * 40 * <h3>Example</h3> 41 * 42 * <p>A {@link WakefulBroadcastReceiver} uses the method 43 * {@link WakefulBroadcastReceiver#startWakefulService startWakefulService()} 44 * to start the service that does the work. This method is comparable to 45 * {@link android.content.Context#startService startService()}, except that 46 * the {@link WakefulBroadcastReceiver} is holding a wake lock when the service 47 * starts. The intent that is passed with 48 * {@link WakefulBroadcastReceiver#startWakefulService startWakefulService()} 49 * holds an extra identifying the wake lock.</p> 50 * 51 * {@sample frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/content/SimpleWakefulReceiver.java 52 * complete} 53 * 54 * <p>The service (in this example, an {@link android.app.IntentService}) does 55 * some work. When it is finished, it releases the wake lock by calling 56 * {@link WakefulBroadcastReceiver#completeWakefulIntent 57 * completeWakefulIntent(intent)}. The intent it passes as a parameter 58 * is the same intent that the {@link WakefulBroadcastReceiver} originally 59 * passed in.</p> 60 * 61 * {@sample frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/content/SimpleWakefulService.java 62 * complete} 63 * 64 * @deprecated As of {@link android.os.Build.VERSION_CODES#O Android O}, background check 65 * restrictions make this class no longer generally useful. (It is generally not safe to 66 * start a service from the receipt of a broadcast, because you don't have any guarantees 67 * that your app is in the foreground at this point and thus allowed to do so.) Instead, 68 * developers should use android.app.job.JobScheduler to schedule a job, and this 69 * does not require that the app hold a wake lock while doing so (the system will take 70 * care of holding a wake lock for the job). 71 */ 72 @Deprecated 73 public abstract class WakefulBroadcastReceiver extends BroadcastReceiver { 74 private static final String EXTRA_WAKE_LOCK_ID = "androidx.contentpager.content.wakelockid"; 75 76 private static final SparseArray<PowerManager.WakeLock> sActiveWakeLocks = new SparseArray<>(); 77 private static int mNextId = 1; 78 79 /** 80 * Do a {@link android.content.Context#startService(android.content.Intent) 81 * Context.startService}, but holding a wake lock while the service starts. 82 * This will modify the Intent to hold an extra identifying the wake lock; 83 * when the service receives it in {@link android.app.Service#onStartCommand 84 * Service.onStartCommand}, it should pass back the Intent it receives there to 85 * {@link #completeWakefulIntent(android.content.Intent)} in order to release 86 * the wake lock. 87 * 88 * @param context The Context in which it operate. 89 * @param intent The Intent with which to start the service, as per 90 * {@link android.content.Context#startService(android.content.Intent) 91 * Context.startService}. 92 */ startWakefulService(Context context, Intent intent)93 public static ComponentName startWakefulService(Context context, Intent intent) { 94 synchronized (sActiveWakeLocks) { 95 int id = mNextId; 96 mNextId++; 97 if (mNextId <= 0) { 98 mNextId = 1; 99 } 100 101 intent.putExtra(EXTRA_WAKE_LOCK_ID, id); 102 ComponentName comp = context.startService(intent); 103 if (comp == null) { 104 return null; 105 } 106 107 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 108 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 109 "androidx.core:wake:" + comp.flattenToShortString()); 110 wl.setReferenceCounted(false); 111 wl.acquire(60 * 1000); 112 sActiveWakeLocks.put(id, wl); 113 return comp; 114 } 115 } 116 117 /** 118 * Finish the execution from a previous {@link #startWakefulService}. Any wake lock 119 * that was being held will now be released. 120 * 121 * @param intent The Intent as originally generated by {@link #startWakefulService}. 122 * @return Returns true if the intent is associated with a wake lock that is 123 * now released; returns false if there was no wake lock specified for it. 124 */ completeWakefulIntent(Intent intent)125 public static boolean completeWakefulIntent(Intent intent) { 126 final int id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, 0); 127 if (id == 0) { 128 return false; 129 } 130 synchronized (sActiveWakeLocks) { 131 PowerManager.WakeLock wl = sActiveWakeLocks.get(id); 132 if (wl != null) { 133 wl.release(); 134 sActiveWakeLocks.remove(id); 135 return true; 136 } 137 // We return true whether or not we actually found the wake lock 138 // the return code is defined to indicate whether the Intent contained 139 // an identifier for a wake lock that it was supposed to match. 140 // We just log a warning here if there is no wake lock found, which could 141 // happen for example if this function is called twice on the same 142 // intent or the process is killed and restarted before processing the intent. 143 Log.w("WakefulBroadcastReceiv.", "No active wake lock id #" + id); 144 return true; 145 } 146 } 147 } 148