Excessive Alarm Manager wakeups in android with Google Play Services Location
Asked Answered
P

1

28

I received an performance report from Android Vital on Google Play Console about Excessive Alarm Manager wakeups:

https://developer.android.com/topic/performance/vitals/wakeup.html

I use Location API from Google Play Services to request for location updates in the background. On the report it show that the excessive wake up wakeups was caused by com.google.android.location.ALARM_WAKEUP_LOCATOR from LocationListener.

Below the code snippet which causes the alarms:

private synchronized void buildGoogleApiClient() {
    mGoogleApiClient = new GoogleApiClient.Builder(context)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();
    mGoogleApiClient.connect();
}

/**
 * Runs when a GoogleApiClient object successfully connects.
 */
@Override
public void onConnected(Bundle connectionHint) {
    try {
        // Set location request
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(5 * 60000);
        mLocationRequest.setFastestInterval(60000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        mLocationRequest.setMaxWaitTime(30 * 60000);

        // Create a pending intent to listening on location service when the update become available
        Intent mIntent = new Intent(context, LocationReceiver.class);
        mIntent.setAction(LocationReceiver.LOCATION_EXTRA);
        mPendingIntent = PendingIntent.getBroadcast(context, 42, mIntent, PendingIntent.FLAG_CANCEL_CURRENT);
        // Permission check before launching location services
        if (ContextCompat.checkSelfPermission(context,
                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mPendingIntent);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Is this alarm wakeup linked to the Location API from google play services?

Is anyone know how to fix this issue?

Probate answered 8/9, 2017 at 11:53 Comment(4)
On the same link you've provided, have you actually tried the suggested 'fixes' ?Rembert
@noogui I have unfortunately no control on Google Play Sevices. Let me know if you have any idea on how to modify the behavior of Google Play Services.Probate
what do you need control over play services for? These are app-level fixes they suggest.Spinelli
@YvetteColomb I do not have anything else in my code beside a call to play services to get location updates regularly.Probate
T
34

The Google Location api

This is an issue with the com.google.android.location.ALARM_WAKEUP_LOCATOR waking up devices every 60 seconds and keeping them awake for up to 15 seconds, causing major battery drainage issues.

There's been fixes for phone uses via external apps and teaching users how to actually revoke permission for an app.

The Solution

The following provides the tools to reduce the battery usage of the app.

It's impossible to provide a customised solution for the app, as the solution depends upon the use case and business logic of the app and a cost benefit analysis of what you wish to achieve with the location updates.

Time intervals

It's no surprise there's battery performance issues. From the following settings:

mLocationRequest.setInterval(5 * 60000);
mLocationRequest.setFastestInterval(60000);
mLocationRequest.setMaxWaitTime(30 * 60000);

Your interval is set to update every 5 minutes (5 * 60000ms). That's 24 hours a day. If this updates successfully every 5 minutes: 12 times/hour == 288 times per day.

The fastest interval being set at 1 minute (60000). Although that is available because location is accessed elsewhere on the device, it will still use power in your app).

The maxwaittime is only 30 minutes. Which means at best the device will be woken up and polled a minimum of 48 times per day.

Increase these times.

setMaxWaitTime ... This can consume less battery and give more accurate locations, depending on the device's hardware capabilities. You should set this value to be as large as possible for your needs if you don't need immediate location delivery. ...

In Android 8 Google has limited the number of requests to only a few per hour. Consider using this as a guideline in setting intervals for requests.

Limiting number of updates

The number of updates within a certain time frame can be limited. By either actively cancelling the location request once the number of updates have been used or setting an expiration on the request. This way the app can be managing by creating a request on some trigger within the app and carefully managed so that it doesn't continue endlessly. It's difficult to create a flow, as I don't know the use case for the app.

setNumUpdates (int numUpdates)

By default locations are continuously updated until the request is explicitly removed, however you can optionally request a set number of updates. For example, if your application only needs a single fresh location, then call this method with a value of 1 before passing the request to the location client.

Stop location updates

Another option is to manage stop locations updates when they are not required. The links gives examples of calling this when an activity loses focus, however it could be implemented within the app, when certain requirements are met (that's within your business logic) or even by giving the user to turn it on and off from within the app itself.

Battery optimisation

Ensure your app is not ignoring battery optimizations.

Displacement

Also managing setSmallestDisplacement (float smallestDisplacementMeters) will help to fine tune the app, depending on the business logic.

the following was written before the updated question.

How often the app updates can be set by both the developer and the user. The intervals and priorities.

For the developer.

You can set these when making a location request for example:

protected void createLocationRequest() {
    LocationRequest mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(10000);
    mLocationRequest.setFastestInterval(5000);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
}

There is also a setting for PRIORITY_NO_POWER, which means the app will only get updates when the user asks for them.

For the user.

You will need to Prompt the User to Change Location Settings

task.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
    @Override
    public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
        // All location settings are satisfied. The client can initialize
        // location requests here.
        // ...
    }
});

task.addOnFailureListener(this, new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        int statusCode = ((ApiException) e).getStatusCode();
        switch (statusCode) {
            case CommonStatusCodes.RESOLUTION_REQUIRED:
                // Location settings are not satisfied, but this can be fixed
                // by showing the user a dialog.
                try {
                    // Show the dialog by calling startResolutionForResult(),
                    // and check the result in onActivityResult().
                    ResolvableApiException resolvable = (ResolvableApiException) e;
                    resolvable.startResolutionForResult(MainActivity.this,
                            REQUEST_CHECK_SETTINGS);
                } catch (IntentSender.SendIntentException sendEx) {
                    // Ignore the error.
                }
                break;
            case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                // Location settings are not satisfied. However, we have no way
                // to fix the settings so we won't show the dialog.
                break;
        }
    }
});

To manage changes in user setting use a location call back.

... you can use the new LocationCallback class in place of your existing LocationListener to receive LocationAvailability updates in addition to location updates, giving you a simple callback whenever settings might have changed which will affect the current set of LocationRequests.

Google has addressed this issue in Android 8.

In an effort to reduce power consumption, Android 8.0 (API level 26) limits how frequently background apps can retrieve the user's current location. Apps can receive location updates only a few times each hour.

Note: These limitations apply to all apps used on devices running Android 8.0 (API level 26) or higher, regardless of an app's target SDK version.


If using the Alarm Manager.

This can be related to the Alarm Manager.

It's not a simple fix, ultimately you'll need to rewrite how you are scheduling your updates.

  1. To at least slow down the problem you'll need to debug your code and find any instances where the Alarm Manager calls or creates schedules and reduce the intervals.

  2. After that you will need to rewrite the entire location update scheduling part of the app.

Fix the Problem

Identify the places in your app where you schedule wakeup alarms and reduce the frequency that those alarms are triggered. Here are some tips:

  • Look for calls to the various set() methods in AlarmManager that include either the RTC_WAKEUP or ELAPSED_REALTIME_WAKEUP flag.
  • We recommend including your package, class, or method name in your alarm's tag name so that you can easily identify the location in your source where the alarm was set. Here are some additional tips:
    • Leave out any personally identifying information (PII) in the name, such as an email address. Otherwise, the device logs _UNKNOWN instead of the alarm name.
    • Don't get the class or method name programmatically, for example by calling getName(), because it could get obfuscated by Proguard. Instead use a hard-coded string.
    • Don't add a counter or unique identifiers to alarm tags. The system will not be able to aggregate alarms that are set that way because they all have unique identifiers.

After fixing the problem, verify that your wakeup alarms are working as expected by running the following ADB command:

adb shell dumpsys alarm

This command provides information about the status of the alarm system service on the device. For more information, see dumpsys.

If you have particular issues with any of the above, you'll need to post another question with an mcve.

To improve the app, it would involve rewriting the code as mentioned here in Best Practices. Don't use the alarm manager to schedule background tasks, and location would be considered a background task, if the phone is asleep. Plus you say it's a background task. Use JobScheduler or Firebase JobDispatcher.

If the Alarm Manager is the better choice (which it's not here) it's important to have a good read here Scheduling Repeating Alarms, but you need to Understand the Trade-offs

A poorly designed alarm can cause battery drain and put a significant load on servers.

Thamos answered 12/9, 2017 at 8:51 Comment(2)
I do not have any instance of Alarm Manager in my code. I am only using the play services to get location updates.Probate
I really wonder how it's gonna work with location update limitation if an app requires location updates frequently such as location tracking app. How will we calculate speed or route of the device if it's not possible to get location updates more than 4 times per hour?Expressway

© 2022 - 2024 — McMap. All rights reserved.