9

I am having some trouble with activity recognition. I have implemented it in an app and it works fine when the device's screen is on. I have a log entry in my Activity recognition intent service class and I can see when it gets an update.So, I know it is working fine when the screen is on.

But then after the phone is put to standby(the screen is turned off) it stops detecting the uses activity.

The onDisconnected() in the DetectionRequester class is not getting called, I checked using a log post.

My question: Why does my app stop tracking the uses activity after the device goes to standby mode? And how do I make it not stop detecting the users activity?

Let me know if you need to see any of the code or if you need any more details on my app.

Relevant bits of code from my MainActivity class. This is where the app starts the request for ActivityRecognition.

public class MainActivity extends FragmentActivity  {

// The activity recognition update request object
private DetectionRequester mDetectionRequester;

// The activity recognition update removal object
private DetectionRemover mDetectionRemover;


// Store the current request type (ADD or REMOVE)
private REQUEST_TYPE mRequestType;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);     
    //Crashlytics.start(this);
    setContentView(R.layout.activity_main);

    mDetectionRequester = new DetectionRequester(this);
    mDetectionRemover = new DetectionRemover(this);

    // Check for Google Play services
     if (servicesConnected()) 
     {
        /*
         *Set the request type. If a connection error occurs, and Google Play services can
         * handle it, then onActivityResult will use the request type to retry the request
         */

          mRequestType = ActivityUtils.REQUEST_TYPE.ADD;

         // Pass the update request to the requester object
          mDetectionRequester.requestUpdates();

      }
}

 private boolean servicesConnected() {

    Log.wtf("Rakshak", "Service connected method");

    // Check that Google Play services is available
    int resultCode =
            GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

    // If Google Play services is available
    if (ConnectionResult.SUCCESS == resultCode) 
    {
        Log.wtf("Rakshak", "Service connected method: connection result success");
        // Continue
        return true;

    // Google Play services was not available for some reason
    } else {

        Log.wtf("Rakshak", "Service connected method: connection result failure");

        // Display an error dialog
        GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0).show();
        return false;
    }
}        


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {

    // Choose what to do based on the request code
    switch (requestCode) {

        // If the request code matches the code sent in onConnectionFailed
        case ActivityUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST :

            switch (resultCode) {
                // If Google Play services resolved the problem
                case Activity.RESULT_OK:

                    // If the request was to start activity recognition updates
                    if (ActivityUtils.REQUEST_TYPE.ADD == mRequestType) {

                        // Restart the process of requesting activity recognition updates

                        mDetectionRequester.requestUpdates(); 

                    // If the request was to remove activity recognition updates
                    } else if (ActivityUtils.REQUEST_TYPE.REMOVE == mRequestType ){

                            /*
                             * Restart the removal of all activity recognition updates for the 
                             * PendingIntent.
                            */
                           // mDetectionRemover.removeUpdates(
                              //  mDetectionRequester.getRequestPendingIntent()); 

                    }
                break;

                // If any other result was returned by Google Play services
                default:

                    // Report that Google Play services was unable to resolve the problem.
                    Log.d(ActivityUtils.APPTAG, "unable to resolve Google play services problems");
            }

        // If any other request code was received
        default:
           // Report that this Activity received an unknown requestCode
           Log.d(ActivityUtils.APPTAG,
                   "received an unknown request code");

           break;
    }
 }

My DetectionRequester class:

public class DetectionRequester
    implements ConnectionCallbacks, OnConnectionFailedListener {

// Storage for a context from the calling client
private Context mContext;

// Stores the PendingIntent used to send activity recognition events back to the app
private PendingIntent mActivityRecognitionPendingIntent;

// Stores the current instantiation of the activity recognition client
private ActivityRecognitionClient mActivityRecognitionClient;

public DetectionRequester(Context context) {
    // Save the context
    mContext = context;

    // Initialize the globals to null
    mActivityRecognitionPendingIntent = null;
    mActivityRecognitionClient = null;

}
/**
 * Returns the current PendingIntent to the caller.
 *
 * @return The PendingIntent used to request activity recognition updates
 */
public PendingIntent getRequestPendingIntent() {
    return mActivityRecognitionPendingIntent;
}

/**
 * Sets the PendingIntent used to make activity recognition update requests
 * @param intent The PendingIntent
 */
public void setRequestPendingIntent(PendingIntent intent) {
    mActivityRecognitionPendingIntent = intent;
}

/**
 * Start the activity recognition update request process by
 * getting a connection.
 */
public void requestUpdates() {
    requestConnection();
}

/**
 * Make the actual update request. This is called from onConnected().
 */
private void continueRequestActivityUpdates() {
    /*
     * Request updates, using the default detection interval.
     * The PendingIntent sends updates to ActivityRecognitionIntentService
     */
    getActivityRecognitionClient().requestActivityUpdates(
            ActivityUtils.DETECTION_INTERVAL_MILLISECONDS,
            createRequestPendingIntent());

    // Disconnect the client
    requestDisconnection();
}

/**
 * Request a connection to Location Services. This call returns immediately,
 * but the request is not complete until onConnected() or onConnectionFailure() is called.
 */
private void requestConnection() {
    getActivityRecognitionClient().connect();
}

/**
 * Get the current activity recognition client, or create a new one if necessary.
 * This method facilitates multiple requests for a client, even if a previous
 * request wasn't finished. Since only one client object exists while a connection
 * is underway, no memory leaks occur.
 *
 * @return An ActivityRecognitionClient object
 */
private ActivityRecognitionClient getActivityRecognitionClient() {
    if (mActivityRecognitionClient == null) {

        mActivityRecognitionClient =
                new ActivityRecognitionClient(mContext, this, this);
    }
    return mActivityRecognitionClient;
}

/**
 * Get the current activity recognition client and disconnect from Location Services
 */
private void requestDisconnection() {
    getActivityRecognitionClient().disconnect();
}

/*
 * Called by Location Services once the activity recognition client is connected.
 *
 * Continue by requesting activity updates.
 */
@Override
public void onConnected(Bundle arg0) {
    // If debugging, log the connection
    Log.w("Rakshak", "Locatin client connected");

    // Continue the process of requesting activity recognition updates
    continueRequestActivityUpdates();
}

/*
 * Called by Location Services once the activity recognition client is disconnected.
 */
@Override
public void onDisconnected() {
    // In debug mode, log the disconnection
     Log.w("Rakshak", "Locatin client dis-connected");

    // Destroy the current activity recognition client
    mActivityRecognitionClient = null;

}

/**
 * Get a PendingIntent to send with the request to get activity recognition updates. Location
 * Services issues the Intent inside this PendingIntent whenever a activity recognition update
 * occurs.
 *
 * @return A PendingIntent for the IntentService that handles activity recognition updates.
 */
private PendingIntent createRequestPendingIntent() {

    // If the PendingIntent already exists
    if (null != getRequestPendingIntent()) {

        // Return the existing intent
        return mActivityRecognitionPendingIntent;

    // If no PendingIntent exists
    } else {
        // Create an Intent pointing to the IntentService
        Intent intent = new Intent(mContext, ActivityRecognitionIntentService.class);

        /*
         * Return a PendingIntent to start the IntentService.
         * Always create a PendingIntent sent to Location Services
         * with FLAG_UPDATE_CURRENT, so that sending the PendingIntent
         * again updates the original. Otherwise, Location Services
         * can't match the PendingIntent to requests made with it.
         */
        PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        setRequestPendingIntent(pendingIntent);
        return pendingIntent;
    }

}

/*
 * Implementation of OnConnectionFailedListener.onConnectionFailed
 * If a connection or disconnection request fails, report the error
 * connectionResult is passed in from Location Services
 */
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    /*
     * Google Play services can resolve some errors it detects.
     * If the error has a resolution, try sending an Intent to
     * start a Google Play services activity that can resolve
     * error.
     */
    if (connectionResult.hasResolution()) {

        try {
            connectionResult.startResolutionForResult((Activity) mContext,
                ActivityUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);

        /*
         * Thrown if Google Play services canceled the original
         * PendingIntent
         */
        } catch (SendIntentException e) {
           // display an error or log it here.
        }

    /*
     * If no resolution is available, display Google
     * Play service error dialog. This may direct the
     * user to Google Play Store if Google Play services
     * is out of date.
     */
    } else {
        Dialog dialog = GooglePlayServicesUtil.getErrorDialog(
                        connectionResult.getErrorCode(),
                        (Activity) mContext,
                        ActivityUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);
        if (dialog != null) {
            dialog.show();
        }
    }
}

}

The intent service:

public class ActivityRecognitionIntentService extends IntentService {   

public ActivityRecognitionIntentService() {
    // Set the label for the service's background thread
    super("ActivityRecognitionIntentService");
}

@Override
protected void onHandleIntent(Intent intent) {

    Log.w("Rakshak", "the on handel intent called"); // I see this only when the devices screen is on 

    // do some fun stuff

  }
DrkStr
  • 1,226
  • 2
  • 25
  • 62
  • does `onPause()` get called? – sschrass Sep 03 '14 at 14:04
  • onPause() for what ? I don't think intent services have onPause(). My Activity recognition gets called when the the user first launches the app. – DrkStr Sep 03 '14 at 15:27
  • Oh you are right, there it is "intent service" well hidden between all the "activity" and "app". – sschrass Sep 03 '14 at 15:57
  • My apologies for the confusing explanation. – DrkStr Sep 03 '14 at 16:07
  • if i understood clear, maybe you need to try keep screen on via using flags. For this and more. check the link: https://developer.android.com/training/scheduling/wakelock.html – Umit Kaya Sep 04 '14 at 08:15
  • No, I am not trying to keep the screen on. I am trying to keep receiving activity updates even if the screen in not on. – DrkStr Sep 04 '14 at 08:26
  • 1
    I just tested this on a Nexus 7 and it calls my IntentService even if the screen is turned on so I don't think activity recognition stops when the screen is off (I also never observed this in my various tests in the past with different devices). Please post your IntentService code and also how you initialize it including the two listeners you pass into the ActivityRecognitionClient. – Emanuel Moecklin Sep 04 '14 at 18:48
  • @EmanuelMoecklin I have added the requested code. Thanks for having a look. Do let me know if you need to see anymore of the code. The requestConnection() is called from the app's MainActivity when the app is first created. – DrkStr Sep 04 '14 at 19:09
  • 1
    A couple of methods are still missing from your code (requestDisconnection, setRequestPendingIntent etc.) but I'd say the reason for this issue is that the PendingIntent is created with FLAG_CANCEL_CURRENT instead of FLAG_UPDATE_CURRENT. This plus the fact that you're probably using different instance variables (mActivityRecognitionClient, mActivityRecognitionPendingIntent etc.) instead of static variables means the PendingIntent is probably created more than once and so the location service won't be able to match the PendingIntent to the service any more. – Emanuel Moecklin Sep 04 '14 at 19:49
  • 1
    Please use FLAG_UPDATE_CURRENT and if that doesn't help post the complete code including the Activity life cycle methods like onCreate, onStart etc., all missing methods related to activity recognition plus the definition of used variables (mActivityRecognitionClient...). The fact that you don't get a onDisconnect() call means there's something wrong there too (it should be called when you disconnect from the ActivityRecognitionClient by calling your requestDisconnection()). – Emanuel Moecklin Sep 04 '14 at 19:54
  • Changed to FLAG_UPDATE_CURRENT, did not help. I have posted the entire DetectionRequester class.The activity detection request starts in the onCreate of my MainActivity, I have add bits of my MainActivity that I think are relevant. I would like to add that I get the user activity updates just fine when the devices screen in on. It is only when the devices screen is off that the updates stop. I start getting activity updates again when I turn the screen back on again. Thanks again for having a look. Let me know if you need to see anymore of the code or if you need any-more info. – DrkStr Sep 04 '14 at 20:13
  • 1
    I tested your code as is and it works even with screen turned off. I tested on a Moto G and a Nexus 7. What test device do you use? You don't happen to use a Sony device with Stamina mode enabled? Also does your device have location services enabled? Any other app that would turn off location services when the device goes to sleep? – Emanuel Moecklin Sep 04 '14 at 20:50
  • No I dont have any battery saving thing that would turn off Location Services. I even tried completely un-installing and re installing the app and that did not help :( Can you think of anything else that might cause this from happening. I have tested this on a Moto G and Huawei. Having the same problem in both. The worst part about this is that the app was working fine a few weeks ago :( – DrkStr Sep 04 '14 at 21:15
  • 1
    What Android version is your Moto G running? What's your DETECTION_INTERVAL_MILLISECONDS? Where and how do you use your mDetectionRemover? – Emanuel Moecklin Sep 04 '14 at 21:29
  • The Moto G is running 4.4.4 stock (no custom ROM). The DETECTION_INTERVAL_MILLISECONDS is 20,000(20 secs). I have not implemented a mDetectionRemover coz I need to Activity Recognition to be running permanently. – DrkStr Sep 05 '14 at 04:37
  • I tried making a standalone Activity recognition test app and installed in on my phones and I am facing the same issue, it works fine when the screen is on but stops working when the screen is off. Can I send you the source code for my standalone Activity recognition test app so you can see if I have done something wrong? My email id is r.rakshak@gmail.com send me you an email and I will send you the source code. – DrkStr Sep 05 '14 at 07:00
  • onegravityllc@yahoo.com – Emanuel Moecklin Sep 05 '14 at 12:59
  • Thankyou. Sending you the code now. – DrkStr Sep 05 '14 at 13:08
  • 1
    Any luck on this one? – Emanuel Moecklin Sep 09 '14 at 13:38
  • You where right, it was my phone that paused activity updates when the screen is turned off. Can you put the ans as a question so I can give you the bounty? – DrkStr Sep 09 '14 at 19:19
  • Strangely enough, I don't have this problem with my app. AND, I don't have location services on. My app gets activity updates when the phone is in standby/sleep and location services off. I assume that the underlying activity recognition service is using a wake lock or something so that activity updates are delivered in standby/sleep mode. One difference from the OP's codes is I use the Activity Recognition API, via GoogleApiClient, not via ActivityRecognitionClient. – mbeaty Sep 30 '14 at 01:59
  • Hello, I got the same probs... I read the whole page (including all comments), is there anything new? I guess under this circumstances is the API VERY USELESS -.- I mean "on bike" will only recognized when the screen is on?! oO I don't know one single person who has the phones screen on when riding a bike :D :D – Martin Pfeffer Dec 18 '14 at 17:03
  • 2
    @MartinPfeffer: this is an issue only on some low end devices. The API works very well with the screen off on 90% of devices. We have implemented this in our app and we are very happy with the results. (app here: google.com/+drvrapp) Also, I would strongly recommend against reading the comments and posts on this, they are garbage. – DrkStr Dec 20 '14 at 05:12
  • Thanks for your advice. In my app I need nearly 100% reliability, so I did a trick with the fused location provider itself, it seems to work and is quite simple implemented... but I need to do some testings to be sure it works as well.. here is the snipped: if (location.getSpeed() > 2) movementRoutine(); else if (!location.hasSpeed() || location.getSpeed() <= 2) noMovementRoutine(); If it will fail, I will implement the ActivityRecognizer, too. Thanks :) PS: Your app looks very nice ;) I will test it, if you invite me to the beta testers my mail: martinpaush@gmail.com ;) – Martin Pfeffer Dec 20 '14 at 16:09
  • Sent you an invite. You will get a mail from apps@crashlytics.com with the invite. Check your spam box, it may end up in there. – DrkStr Dec 21 '14 at 17:16
  • @EmanuelMoecklin "I tested your code as is and it works even with screen turned off. " The question is how long did you wait with screen off. It may differ on different devices. – Marian Paździoch Oct 27 '15 at 09:10
  • @DrkStr "this is an issue only on some low end devices. The API works very well with the screen off on 90% of devices." it does NOT work on my HTM M8 see http://stackoverflow.com/questions/33363274/activity-recognition-does-not-work-after-phone-goes-asleep which is rather not low end! – Marian Paździoch Oct 27 '15 at 09:14

2 Answers2

2

To avoid draining the battery, an Android device that is left idle quickly falls asleep. It's the reason of your service stopping. You can read more about how it can be handled here: Keeping the Device Awake. Also take a look on WakefulIntentService from cwac-wakeful library. Looks like it's what you are looking for.

amukhachov
  • 5,566
  • 1
  • 37
  • 55
1

Take a look at "Repeating Alarms":

They operate outside of your application, so you can use them to trigger events or actions even when your app is not running, and even if the device itself is asleep.

https://developer.android.com/training/scheduling/alarms.html

Or at "WakeLock":

One legitimate case for using a wake lock might be a background service that needs to grab a wake lock to keep the CPU running to do work while the screen is off. Again, though, this practice should be minimized because of its impact on battery life.

Specially "WakefulBroadcastReceiver":

Using a broadcast receiver in conjunction with a service lets you manage the life cycle of a background task.

A WakefulBroadcastReceiver is a special type of broadcast receiver that takes care of creating and managing a PARTIAL_WAKE_LOCK for your app. A WakefulBroadcastReceiver passes off the work to a Service (typically an IntentService), while ensuring that the device does not go back to sleep in the transition. If you don't hold a wake lock while transitioning the work to a service, you are effectively allowing the device to go back to sleep before the work completes. The net result is that the app might not finish doing the work until some arbitrary point in the future, which is not what you want.

https://developer.android.com/training/scheduling/wakelock.html#cpu

If you consider to use the second approach be careful about draining the battery...

Community
  • 1
  • 1
semsamot
  • 785
  • 7
  • 11
  • 1
    The Location API should wake the device up by-itself to delver the activity update. – DrkStr Sep 04 '14 at 08:33
  • @DrkStr: Did you find a solution for this? – decades Nov 13 '14 at 21:37
  • No, on some lower-end phones the Activity Recognition Intent Service just stops doing updates when the screen is turned off. I a not sure how to differentiate these phones so I can filter them out using the "User feature" element in the Android Manifest. – DrkStr Nov 14 '14 at 07:43
  • @DrkStr: I'm having made exactly the same sad experience. It works fine on my Nexus 5 with 4.4, even if the screen is locked. It stops doing anything until return to life on an Android Watch, 4.2.2 :(( EDIT: BTW: I tried both approaches, so also the Google Play Service Activitiy Recognition doesn't do the job well... – decades Nov 15 '14 at 11:49
  • 1
    When it works, it works well. But when it doesn't there is on way to know why and there is no alternative either :( – DrkStr Nov 15 '14 at 18:06