android_wakelock/
lib.rs

1//! Safe and ergonomic Rust bindings to the [Android WakeLock
2//! API](https://developer.android.com/reference/android/os/PowerManager#newWakeLock(int,%20java.lang.String)).
3//! Wake locks allow an app or service to keep an Android device's display or
4//! processor awake in order to complete some work. For more information about
5//! wake locks, see the official [Android
6//! guide](https://developer.android.com/training/scheduling/wakelock).
7//!
8//! In short: **device battery life may be significantly affected by the use of
9//! this API**. Do not acquire `WakeLock`s unless you really need them, use the
10//! minimum levels possible, and be sure to release them as soon as possible.
11//!
12//! # Platform support
13//!
14//! This library should work with all Android API levels. It cannot be used on any
15//! other operating system, of course.
16//!
17//! # Creating wake locks
18//!
19//! The simplest way to create a wake lock is to use the [`partial`] function,
20//! which creates a [partial][`Level::Partial`] wake lock configured with
21//! reasonable defaults. This is the lowest level of wake lock, and is the most
22//! friendly to battery life while still keeping the device awake to perform
23//! computation.
24//!
25//! If you want to create a wake lock with a different level or with different
26//! flags, you can use [`WakeLock::builder`] to create a [`Builder`] that
27//! provides methods for setting other supported wake lock options.
28//!
29//! Creating a wake lock from Rust is a somewhat expensive operation, so it is
30//! better to create your wake locks up front and reuse them during your app's
31//! runtime as needed instead of creating them on-demand.
32//!
33//! # Acquiring and releasing wake locks
34//!
35//! Wake locks remain dormant until they are acquired. To acquire a wake lock,
36//! call [`acquire`][WakeLock::acquire] on the wake lock. This will return a
37//! guard object that will keep the wake lock acquired until it is dropped:
38//!
39//! ```no_run
40//! // Create the wake lock.
41//! let wake_lock = android_wakelock::partial("myapp:mytag")?;
42//!
43//! // Start keeping the device awake.
44//! let guard = wake_lock.acquire()?;
45//!
46//! // Do some work while the device is awake...
47//!
48//! // Release the wake lock to allow the device to sleep again.
49//! drop(guard);
50//!
51//! # Ok::<(), Box<dyn std::error::Error>>(())
52//! ```
53//!
54//! Multiple threads can share the same wake lock and acquire it concurrently. As
55//! long as at least one thread has acquired the wake lock, the device will be
56//! kept awake.
57//!
58//! ```no_run
59//! use std::{sync::Arc, thread};
60//!
61//! // Create the wake lock.
62//! let wake_lock = Arc::new(android_wakelock::partial("myapp:mytag")?);
63//! let wake_lock_clone = wake_lock.clone();
64//!
65//! // Spawn multiple threads that use the same wake lock to keep the device awake
66//! // while they do some work.
67//! let worker1 = thread::spawn(move || {
68//!     // Keep the device awake while this worker runs.
69//!     let _guard = wake_lock_clone.acquire().unwrap();
70//!
71//!     // Do some work...
72//! });
73//! let worker2 = thread::spawn(move || {
74//!     // Keep the device awake while this worker runs.
75//!     let _guard = wake_lock.acquire().unwrap();
76//!
77//!     // Some more work...
78//! });
79//!
80//! worker1.join().unwrap();
81//! worker2.join().unwrap();
82//!
83//! # Ok::<(), Box<dyn std::error::Error>>(())
84//! ```
85
86#![warn(
87    future_incompatible,
88    missing_debug_implementations,
89    missing_docs,
90    unreachable_pub,
91    unused,
92    clippy::all
93)]
94
95use std::fmt;
96
97use jni::{
98    objects::{GlobalRef, JObject, JValue},
99    AttachGuard, JavaVM,
100};
101
102const ACQUIRE_CAUSES_WAKEUP: i32 = 0x10000000;
103const ON_AFTER_RELEASE: i32 = 0x20000000;
104
105/// An error returned by the wake lock API. A variety of errors can occur when
106/// calling Android APIs, such as JNI errors, or exceptions actually thrown by the
107/// API itself.
108pub type Error = Box<dyn std::error::Error>;
109
110type Result<T> = std::result::Result<T, Error>;
111
112/// Create a new partial wake lock with the given tag.
113///
114/// This convenience function is equivalent to the following:
115///
116/// ```no_run
117/// use android_wakelock::{Level, WakeLock};
118///
119/// # let tag = "myapp:mytag";
120/// WakeLock::builder(tag)
121///     .level(Level::Partial)
122///     .build();
123/// ```
124pub fn partial<T: Into<String>>(tag: T) -> Result<WakeLock> {
125    WakeLock::builder(tag).build()
126}
127
128/// Possible levels for a wake lock.
129#[repr(i32)]
130#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
131pub enum Level {
132    /// Ensures that the CPU is running; the screen and keyboard backlight will
133    /// be allowed to go off.
134    ///
135    /// If the user presses the power button, then the screen will be turned off
136    /// but the CPU will be kept on until all partial wake locks have been
137    /// released.
138    Partial = 0x00000001,
139
140    /// Ensures that the screen and keyboard backlight are on at full
141    /// brightness.
142    ///
143    /// If the user presses the power button, then the wake lock will be
144    /// implicitly released by the system, causing both the screen and the CPU
145    /// to be turned off. Contrast with [`Level::Partial`].
146    ///
147    /// # Deprecation
148    ///
149    /// **This constant was deprecated in API level 17.** Most applications
150    /// should use `WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON` instead of
151    /// this type of wake lock, as it will be correctly managed by the platform
152    /// as the user moves between applications and doesn't require a special
153    /// permission.
154    #[deprecated]
155    Full = 0x0000001a,
156
157    /// Ensures that the screen is on at full brightness; the keyboard backlight
158    /// will be allowed to go off.
159    ///
160    /// If the user presses the power button, then the wake lock will be
161    /// implicitly released by the system, causing both the screen and the CPU
162    /// to be turned off. Contrast with [`Level::Partial`].
163    ///
164    /// # Deprecation
165    ///
166    /// **This constant was deprecated in API level 15.** Most applications
167    /// should use `WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON` instead of
168    /// this type of wake lock, as it will be correctly managed by the platform
169    /// as the user moves between applications and doesn't require a special
170    /// permission.
171    #[deprecated]
172    ScreenBright = 0x0000000a,
173
174    /// Wake lock level: Ensures that the screen is on (but may be dimmed); the
175    /// keyboard backlight will be allowed to go off.
176    ///
177    /// If the user presses the power button, then the wake lock will be
178    /// implicitly released by the system, causing both the screen and the CPU
179    /// to be turned off. Contrast with [`Level::Partial`].
180    ///
181    /// # Deprecation
182    ///
183    /// **This constant was deprecated in API level 17.** Most applications
184    /// should use `WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON` instead of
185    /// this type of wake lock, as it will be correctly managed by the platform
186    /// as the user moves between applications and doesn't require a special
187    /// permission.
188    #[deprecated]
189    ScreenDim = 0x00000006,
190}
191
192/// A builder for configuring and creating a wake lock.
193#[derive(Clone, Debug)]
194pub struct Builder {
195    tag: String,
196    level: Level,
197    acquire_causes_wakeup: bool,
198    on_after_release: bool,
199}
200
201impl Builder {
202    /// Set the wake lock level.
203    ///
204    /// Generally [`Level::Partial`] wake locks are preferred, and is the
205    /// default level if not specified. See [`Level`] for more information about
206    /// the different available wake lock levels.
207    pub fn level(mut self, level: Level) -> Self {
208        self.level = level;
209        self
210    }
211
212    /// Turn the screen on when the wake lock is acquired.
213    ///
214    /// This flag requires `Manifest.permission.TURN_SCREEN_ON` for apps
215    /// targeting Android version `Build.VERSION_CODES#UPSIDE_DOWN_CAKE` and
216    /// higher.
217    ///
218    /// Normally wake locks don't actually wake the device, they just cause the
219    /// screen to remain on once it's already on. This flag will cause the
220    /// device to wake up when the wake lock is acquired.
221    ///
222    /// Android TV playback devices attempt to turn on the HDMI-connected TV via
223    /// HDMI-CEC on any wake-up, including wake-ups triggered by wake locks.
224    ///
225    /// Cannot be used with [`Level::Partial`].
226    ///
227    /// # Deprecation
228    ///
229    /// **This option was deprecated in API level 33.** Most applications should
230    /// use `R.attr.turnScreenOn` or `Activity.setTurnScreenOn(boolean)`
231    /// instead, as this prevents the previous foreground app from being resumed
232    /// first when the screen turns on.
233    #[deprecated]
234    pub fn acquire_causes_wakeup(mut self, acquire_causes_wakeup: bool) -> Self {
235        self.acquire_causes_wakeup = acquire_causes_wakeup;
236        self
237    }
238
239    /// When this wake lock is released, poke the user activity timer so the
240    /// screen stays on for a little longer.
241    ///
242    /// This will not turn the screen on if it is not already on.
243    ///
244    /// Cannot be used with [`Level::Partial`].
245    pub fn on_after_release(mut self, on_after_release: bool) -> Self {
246        self.on_after_release = on_after_release;
247        self
248    }
249
250    /// Creates a new wake lock with the specified level and options.
251    pub fn build(&self) -> Result<WakeLock> {
252        let ctx = ndk_context::android_context();
253        let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()) }?;
254        let mut env = vm.attach_current_thread()?;
255
256        // Fetch the PowerManager system service.
257        let power_manager_service_id = env.new_string("power")?;
258        let power_manager = catch_exceptions(&mut env, |env| {
259            env.call_method(
260                unsafe { JObject::from_raw(ctx.context().cast()) },
261                "getSystemService",
262                "(Ljava/lang/String;)Ljava/lang/Object;",
263                &[JValue::from(&power_manager_service_id)],
264            )?
265            .l()
266        })?;
267
268        let name = env.new_string(&self.tag)?;
269        let mut flags = self.level as i32;
270
271        if self.acquire_causes_wakeup {
272            flags |= ACQUIRE_CAUSES_WAKEUP;
273        }
274
275        if self.on_after_release {
276            flags |= ON_AFTER_RELEASE;
277        }
278
279        // Create the wake lock.
280        let result = catch_exceptions(&mut env, |env| {
281            env.call_method(
282                &power_manager,
283                "newWakeLock",
284                "(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;",
285                &[JValue::from(flags), JValue::from(&name)],
286            )
287        })?;
288
289        let wake_lock = env.new_global_ref(result.l()?)?;
290
291        drop(env);
292
293        Ok(WakeLock {
294            wake_lock,
295            vm,
296            tag: self.tag.clone(),
297        })
298    }
299}
300
301/// A wake lock is a mechanism to indicate that your application needs to have
302/// the device stay on.
303///
304/// To obtain a wake lock, you can use [`WakeLock::builder`] to configure and
305/// create a wake lock, or you can use [`partial`] to create a partial wake
306/// lock configured with reasonable defaults.
307///
308/// Any application using a `WakeLock` must request the
309/// `android.permission.WAKE_LOCK` permission in an `<uses-permission>` element
310/// of the application's manifest.
311#[derive(Debug)]
312pub struct WakeLock {
313    /// Reference to the underlying Java object.
314    wake_lock: GlobalRef,
315
316    /// The JVM the object belongs to.
317    vm: JavaVM,
318
319    /// The tag specified when the wake lock was created.
320    tag: String,
321}
322
323impl WakeLock {
324    /// Create a new builder with the given tag for configuring and creating a
325    /// wake lock.
326    ///
327    /// # Tags
328    ///
329    /// Your class name (or other tag) for debugging purposes. Recommended
330    /// naming conventions for tags to make debugging easier:
331    ///
332    /// - use a unique prefix delimited by a colon for your app/library (e.g.
333    ///   `gmail:mytag`) to make it easier to understand where the wake locks
334    ///   comes from. This namespace will also avoid collision for tags inside
335    ///   your app coming from different libraries which will make debugging
336    ///   easier.
337    /// - use constants (e.g. do not include timestamps in the tag) to make it
338    ///   easier for tools to aggregate similar wake locks. When collecting
339    ///   debugging data, the platform only monitors a finite number of tags,
340    ///   using constants will help tools to provide better debugging data.
341    /// - avoid wrapping the tag or a prefix to avoid collision with wake lock
342    ///   tags from the platform (e.g. `*alarm*`).
343    /// - never include personally identifiable information for privacy reasons.
344    pub fn builder<T: Into<String>>(tag: T) -> Builder {
345        Builder {
346            tag: tag.into(),
347            level: Level::Partial,
348            acquire_causes_wakeup: false,
349            on_after_release: false,
350        }
351    }
352
353    /// Returns true if the wake lock has outstanding references not yet
354    /// released.
355    pub fn is_held(&self) -> Result<bool> {
356        let mut env = self.vm.attach_current_thread()?;
357
358        catch_exceptions(&mut env, |env| {
359            env.call_method(&self.wake_lock, "isHeld", "()Z", &[])?.z()
360        })
361    }
362
363    /// Acquire the wake lock and force the device to stay on at the level that
364    /// was requested when the wake lock was created.
365    ///
366    /// Returns a [`Guard`] which can be used to release the lock. You should
367    /// release wake locks when you are done and don't need the lock anymore. It
368    /// is very important to do this as soon as possible to avoid running down
369    /// the device's battery excessively.
370    ///
371    /// Wake locks are reference counted like a semaphore and may be acquired
372    /// multiple times by the same or a different thread. The wake lock is not
373    /// released on the device until all acquired references have been released.
374    ///
375    /// # Examples
376    ///
377    /// ```no_run
378    /// // Create the wake lock.
379    /// let wake_lock = android_wakelock::partial("myapp:mytag")?;
380    ///
381    /// // Start keeping the device awake.
382    /// let guard = wake_lock.acquire()?;
383    ///
384    /// // Do some work while the device is awake...
385    ///
386    /// // Release the wake lock to allow the device to sleep again.
387    /// drop(guard);
388    ///
389    /// # Ok::<(), Box<dyn std::error::Error>>(())
390    /// ```
391    pub fn acquire(&self) -> Result<Guard<'_>> {
392        let mut env = self.vm.attach_current_thread()?;
393
394        catch_exceptions(&mut env, |env| {
395            env.call_method(&self.wake_lock, "acquire", "()V", &[])
396        })?;
397
398        log::debug!("acquired wake lock \"{}\"", self.tag);
399
400        Ok(Guard {
401            wake_lock: self.wake_lock.clone(),
402            env,
403            tag: &self.tag,
404        })
405    }
406}
407
408/// A guard for an acquired wake lock.
409///
410/// To create a guard see [`WakeLock::acquire`].
411///
412/// The wake lock is released automatically when the guard is dropped, but
413/// panics if there is an error releasing the wake lock. If you want to handle
414/// errors on release then you can call [`Guard::release`] instead.
415///
416/// The current thread will remain attached to the current JVM until the guard
417/// is released. The guard cannot be sent between threads.
418pub struct Guard<'a> {
419    /// Reference to the underlying Java object.
420    wake_lock: GlobalRef,
421
422    env: AttachGuard<'a>,
423
424    /// The tag specified when the wake lock was created.
425    tag: &'a str,
426}
427
428impl Guard<'_> {
429    /// Releases the wake lock, returning an error if the underlying API threw
430    /// an exception.
431    pub fn release(mut self) -> Result<()> {
432        self.release_one()
433    }
434
435    fn release_one(&mut self) -> Result<()> {
436        catch_exceptions(&mut self.env, |env| {
437            env.call_method(&self.wake_lock, "release", "()V", &[])?;
438
439            log::debug!("released wake lock \"{}\"", self.tag);
440
441            Ok(())
442        })
443    }
444}
445
446impl fmt::Debug for Guard<'_> {
447    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
448        f.debug_struct("Guard")
449            .field("wake_lock", &self.wake_lock)
450            .field("tag", &self.tag)
451            .finish()
452    }
453}
454
455impl Drop for Guard<'_> {
456    fn drop(&mut self) {
457        if let Err(e) = self.release_one() {
458            panic!("error releasing wake lock \"{}\" on drop: {}", self.tag, e);
459        }
460    }
461}
462
463/// Helper for handling Java exceptions thrown when entering Java code that turns
464/// thrown exceptions into formatted Rust errors.
465#[inline]
466fn catch_exceptions<'a, T, F>(env: &mut jni::JNIEnv<'a>, f: F) -> Result<T>
467where
468    F: FnOnce(&mut jni::JNIEnv<'a>) -> jni::errors::Result<T>,
469{
470    match f(env) {
471        Ok(value) => Ok(value),
472        Err(e @ jni::errors::Error::JavaException) => Err({
473            if let Ok(exception) = env.exception_occurred() {
474                let _ = env.exception_clear();
475
476                env.call_method(exception, "getMessage", "()Ljava/lang/String;", &[])
477                    .and_then(|value| value.l())
478                    .and_then(|message| {
479                        env.get_string(&message.into())
480                            .map(|s| s.to_string_lossy().into_owned())
481                    })
482                    .map(|message| message.into())
483                    .unwrap_or_else(|_| e.into())
484            } else {
485                e.into()
486            }
487        }),
488        Err(e) => Err(e.into()),
489    }
490}