3

I have a Broadcast Receiver which handles multiple events. I need it to do some special things for me at boot so I registered the android.intent.action.BOOT_COMPLETED intent, which works fine. If the device is plugged in and is charging the android.intent.action.ACTION_POWER_CONNECTED intent is fired before BOOT_COMPLETED and does work before it's supposed to do stuff. (I'm using BOOT_COMPLETED as a sort of initializer).

Is there a way to check if the BOOT_COMPLETED event has been fired, so that I can run my initializing code in the event something gets fired too early?

JensV
  • 3,997
  • 2
  • 19
  • 43
  • Can you have a boolean that is set when BOOT_COMPLETE is fired? – Steve Smith Feb 27 '17 at 16:03
  • 3
    No, there is no way to do that. Refactor your code to be more flexible. Initialize stuff when you receive the first broadcast instead of when you receive a specific broadcast. Think about changing your architecture, maybe using a component like a `Service` to enforce initialization in the correct order. – Xaver Kapeller Feb 27 '17 at 16:04
  • @SteveSmith that boolean would get unset when the App completely closes/gets force closed and would be invalid if I start the App again. – JensV Feb 27 '17 at 16:06
  • @XaverKapeller Thanks, the actual purpose of this is to log the user out on some special conditions after the device has been restarted. But as it's really important that the App is running it's listening for multiple events and checking if it's alive or not. The power event would start the service and log the device in and the user would be able to hear audio from the login, which is undesired as he's not supposed to be logged in. But you're probably right in that I'm approaching this problem in the wrong way. – JensV Feb 27 '17 at 16:09
  • Why don't you just track the two separately? By like using some flag to track whether or not Boot has actually completed BEFORE processing ACTION_POWER_CONNECTED ? Or have them both trigger the same code, but if one is already running then just ignore it, since its already in the processing state. – JoxTraex Feb 27 '17 at 18:02
  • @JoxTraex The problem is it should only be executed **once** after boot. If the App completely restarts while Android is still running it would get executed again and we'd get undesired behaviour. – JensV Feb 28 '17 at 07:51

3 Answers3

2

I found another solution which is consistent. (I couldn't use another approach as I'm not the one who decides the approach sadly.)

Using the answer from this post: How to get Android system boot time I can save the last boot time in SharedPreferences (or some other storage). Once the boot time is different than the last I know that it's a fresh boot and can apply my initialisations etc.

This is the code I've ended up writing:

private boolean checkIfRebooted(Context context) {
    SharedPreferences prefs = context.getSharedPreferences("package.name.class", Context.MODE_PRIVATE);

    long savedUptime = prefs.getLong("timestamp", 0);
    long currentUptime = System.currentTimeMillis() - SystemClock.elapsedRealtime();

    // Giving it a threshold of 10ms since the calculation may be off by one sometimes.
    if (Math.abs(currentUptime - savedUptime) < 10)
        return false;

    prefs.edit().putLong("timestamp", currentUptime).apply();
    return true;
}
Community
  • 1
  • 1
JensV
  • 3,997
  • 2
  • 19
  • 43
  • Doesn't your answer exact depict the answer given by @Aritra Roy? – azizbekian Feb 28 '17 at 10:00
  • @azizbekian No, because his approach is with a true/false flag based on received events. I can safely determine if it's a new boot and there's no unexpected event where this can go wrong. – JensV Feb 28 '17 at 10:39
  • This solution IMHO checks if there was a reboot since the last check, but not if the BOOT_COMPLETED has already been fired after the last reboot. – Alexey Ozerov Aug 16 '23 at 10:01
1

When the BOOT_COMPLETED is fired, you need to set a flag in your SharedPreferences as "true".

So, now if your ACTION_POWER_CONNECTED gets fired before the BOOT_COMPLETED then you need to check the value of the flag from the SharedPreferences.

If the value is still false, that means that the BOOT_COMPLETED broadcast has not fired yet and you should not perform your actions.

Note - Also remember to reset the flag in SharedPreferences every time.

Aritra Roy
  • 15,355
  • 10
  • 73
  • 107
  • SharedPreferences persist a reboot. How would I know when to reset that flag? – JensV Feb 27 '17 at 16:11
  • You can do that using the ACTION_SHUTDOWN broadcast or if the user starts your app. Both are good candidates to reset the flag. If this answer helps you, please consider accepting and upvoting it. – Aritra Roy Feb 27 '17 at 16:15
  • I can't rely on ACTION_SHUTDOWN because if the whole android system crashes or something unexpected happens and I can't handle the intent. I apprecciate the effort though. Thank you! – JensV Feb 27 '17 at 16:19
  • As I said, you can also reset the flag when the app is launched making it more reliable in that case. – Aritra Roy Feb 27 '17 at 16:21
  • What about the events that come after I've launched the App once? They shouldn't be ignored either... – JensV Feb 27 '17 at 16:35
  • In your use case, they can be. All you need to do it set and reset a flag. – Aritra Roy Feb 27 '17 at 16:40
  • 1
    Can't say I agree with this approach as the state of the flag can definitely go wrong if the user force shuts down. This seems more hacky than anything when there are more appropriate approaches as declared above in the OP's main comments. – JoxTraex Feb 27 '17 at 18:00
0

You could use the kernel boot id from /proc/sys/kernel/random/boot_id. When the code receives BOOT_COMPLETED, store the boot_id in the shared preference. After that it is simple. If the boot_id from /proc is equal to the shared preference, then BOOT_COMPLETED has been handled. Otherwise, not yet.