Reputation: 868
In Android "N", Doze has been extended with "Doze on the Go".
I'm looking for a way to detect when the device enters and leaves these new light doze IDLE and IDLE_MAINTENANCE states. (Basically the same question that was asked for regular Doze here.)
Upvotes: 2
Views: 2354
Reputation: 195
The implementation in TrevorWiley's answer works, but can be simplified a bit. Yes, Nougat's PowerManager has isLightDeviceIdleMode()
and it is annotated with @hide
. We can use reflection to invoke it, which is more succinct and independent of PowerManager's internal implementation details.
public static boolean isLightDeviceIdleMode(final Context context) {
boolean result = false;
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm != null) {
try {
Method isLightDeviceIdleModeMethod = pm.getClass().getDeclaredMethod("isLightDeviceIdleMode");
result = (boolean)isLightDeviceIdleModeMethod.invoke(pm);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
Log.e(TAG, "Reflection failed for isLightDeviceIdleMode: " + e.toString(), e);
}
}
return result;
}
Agreed mostly with TrevorWiley's use of the String to register for Broadcasts. Same as the above method, you could use reflection to grab the value of field ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED
and fall back to the hard-coded String "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED"
.
Upvotes: 4
Reputation: 868
The online documentation for PowerManager doesn't mention it, but the latest source code (API 24 revision 1) has what looks like should be the solution to this question:
String ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED
= "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED"
boolean isLightDeviceIdleMode()
In theory, you could simply register some code as a receiver for the intent and check the current value of the function. Some poking around with dumpsys activity broadcasts
shows that the intent is indeed sent when the light doze state changes.
However, the latest SDK Platform (API 24 revision 2) doesn't have these symbols in it - I get compile errors (and some poking with javap
and jar
shows that they really aren't present). Reaching out to Google, we're told that this is the intended design.
There is a work around, which is to hard code the same string mentioned above and then use reflection to call the same function that would have been called in the API. Like this:
/**
* Check if the device is currently in the Light IDLE mode.
*
* @param context The application context.
* @return True if the device is in the Light IDLE mode.
*/
public static boolean isLightDeviceIdleMode(final Context context) {
boolean result = false;
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm != null) {
// result = pm.isLightDeviceIdleMode();
try {
Log.d(TAG, "Trying reflection for isLightDeviceIdleMode");
Field pmServiceField = pm.getClass().getDeclaredField("mService");
pmServiceField.setAccessible(true);
Object pmService = pmServiceField.get(pm);
Method isLightDeviceIdleMode = pmService.getClass().getDeclaredMethod("isLightDeviceIdleMode");
isLightDeviceIdleMode.setAccessible(true);
result = (Boolean) isLightDeviceIdleMode.invoke(pmService);
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
Log.e(TAG, "Reflection failed for isLightDeviceIdleMode: " + e.toString());
} catch (RemoteException re) {
Log.e(TAG, "Remote exception checking isLightDeviceIdleMode: " + e.toString());
}
}
return result;
}
Upvotes: 5