TrevorWiley TrevorWiley - 2 months ago 27
Android Question

Is it possible to track when an app enters "Doze on the Go" (AKA Doze Light, Doze Extended, and Doze 2)?

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.)

Answer

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;
}