1

I coded an application to record user's location periodically (in the background). I used ActivityRecognitionClient. When activity is received it is compared to previous activity state and is recorded (or not) according to evaluation.

It's working as intended as long as my phone is awake. (log messages appear periodically in LogCat view on eclipse) Whenever screen goes off and device goes into stand by status it stops receiving activity recognition calls. On the other hand, I installed the app on my tablet too, and it keeps updating even the device goes into stand by status. (My phone is General Mobile Discovery btw)

I've been searching web (including stackoverflow questions) for 3 days and haven't been able to find anything that works for me so far. I'd appreciate any help... Thanks...

Following is my applications related code:

AndroidManifest.xml (some permissions are there even if not needed, they are probably leftovers from unsuccessful trials to fix the issue)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.o3n.android.familywathcer"
android:installLocation="internalOnly"
android:versionCode="2"
android:versionName="1.0.1" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="19" />
<uses-feature
    android:glEsVersion="0x00020000"
    android:required="true"/>

<permission android:name="org.o3n.android.familywathcer.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
<uses-permission android:name="org.o3n.android.familywathcer.permission.MAPS_RECEIVE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >

    <meta-data
        android:name="com.google.android.maps.v2.API_KEY"
        android:value="***maps api key *****"/>
    <meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />

    <activity
        android:name="org.o3n.android.familywathcer.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:label="Settings" android:name=".SettingsActivity">
        <intent-filter>
            <action android:name="org.o3n.android.familywatcher.SETTINGS" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

    <service android:enabled="true" android:name=".FamilyWatcherService" />
    <service android:name=".ActivityRecognitionService" />
    <receiver android:name=".StartFamilyWatcherServiceAtBootReceiver"
        android:enabled="true"
        android:exported="true"
        android:label="StartFamilyWatcherServiceAtBootReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
</application>

StartFamilyWatcherServiceAtBootReceiver.java (This receiver starts the FamilyWatcherService.java on device boot, also applications MainActivity.java class calls the FamilyWatcherService so it starts running when first installed.)

public class StartFamilyWatcherServiceAtBootReceiver extends BroadcastReceiver {

private static final String TAG = "o3nWatcherLog";

@Override
public void onReceive(Context context, Intent intent) {
    //Toast.makeText(context, "StartFamilyWatcherServiceAtBootReceiver called", Toast.LENGTH_SHORT).show();
    Log.d(TAG, "StartFamilyWatcherServiceAtBootReceiver onRecieve");

    SettingsRetriever.getInstance(context);

    Intent serviceIntent = new Intent(context, FamilyWatcherService.class);
    context.startService(serviceIntent);
    }
}

FamilyWatcherService.java (This service connects to ActivityRecognitionClient and registers a PendingIntend to be called with activity updates. When it works ActivityRecognitionService.onHandleIntend() method is called)

public class FamilyWatcherService extends Service implements ConnectionCallbacks, OnConnectionFailedListener {

private int period;

private static ActivityRecognitionClient arclient;
private static PendingIntent pIntent;

private static AlarmManager alarmManager; 
private static PendingIntent alarmPI;


private static final String TAG = "o3nWatcherLog";

@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    Log.d(TAG, "FamilyWatcherService onCreate");
    period = SettingsRetriever.getInstance().getPeriod() * 60 * 1000;
}

@Override
public void onDestroy() {
    Log.d(TAG, "FamilyWatcherService onDestroy");
    if(arclient!=null){
        arclient.removeActivityUpdates(pIntent);
        arclient.disconnect();
    }
}

@Override
public void onStart(Intent intent, int startid) {
    Log.d(TAG, "FamilyWatcherService onStart");
    processStart();
}

@Override  
public int onStartCommand(Intent intent, int flags, int startId) {  
    Log.d(TAG, "FamilyWatcherService onStartCommand");
    processStart();
    return Service.START_STICKY;  
} 

public void processStart() {
    int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());
    if (result != ConnectionResult.SUCCESS) {
        Log.d("o3nWatcherLog", "Google Play service is not available (status=" + result + ")");
    }
    else{
        arclient = new ActivityRecognitionClient(getApplicationContext(), this, this);
        arclient.connect();
    }
}

@Override
public void onConnectionFailed(ConnectionResult arg0) {
    Log.d("o3nWatcherLog","Google activity recognition services connection failed");
}

@Override
public void onConnected(Bundle arg0) {
    Log.d("o3nWatcherLog", "FamilyWathcerService onConnected method called...");
    Intent intent = new Intent(this, ActivityRecognitionService.class);
    pIntent = PendingIntent.getService(getApplicationContext(), 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);
    arclient.requestActivityUpdates(period, pIntent);
}

@Override
public void onDisconnected() {
    Log.d("o3nWatcherLog", "Google activity recognition services disconnected");
}

}

ActivityRecognitionService.java (This service's onHandleIntent() method is called by Activity Recognition updates)

package org.o3n.android.familywathcer;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import org.json.JSONException;
import org.json.JSONObject;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;
import com.google.android.gms.location.LocationClient;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;

public class ActivityRecognitionService extends IntentService implements ConnectionCallbacks, OnConnectionFailedListener {

private String TAG = "o3nWatcherLog";

private Context context;

private static int activityEvaluation = 0;

//TODO MAKE THESE PREFERENCES
private static final int MIN_RECORD_DISTANCE = 750;
private static final int MIN_RECORD_INTERVAL = 10 * 1000 * 60;
private static final int MIN_POST_INTERVAL = 2 * 1000 * 60;
//END MAKE THESE PREFERENCES

private LocationClient locationClient;

private static Location lastRecordedLocation;

private static int previousActivityCode = DetectedActivity.UNKNOWN;
private int activityCode = -1000;
private int activityConfidence = -1000;


public ActivityRecognitionService() {
    super("My Activity Recognition Service");
}

@Override
protected void onHandleIntent(Intent intent) {
    if(ActivityRecognitionResult.hasResult(intent)){
        ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
        Log.i(TAG, getType(result.getMostProbableActivity().getType()) +"t" + result.getMostProbableActivity().getConfidence());

        this.context = getApplicationContext();
        Log.d("o3nWatcherLog", "ActivityRecognitionService onHandleIntent called...");

        activityConfidence = result.getMostProbableActivity().getConfidence();
        activityCode = result.getMostProbableActivity().getType();

        Log.d("o3nWatcherLog", " ACTIVITY CODE : " + activityCode + " ACTIVITY CONFIDENCE : " + activityConfidence);

        // Evaluate the avtivity recognition result
        evaluateActivityResult();

        // Get current location
        // check Google Play service APK is available and up to date.
        final int googlePlayServiceAvailable = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
        if (googlePlayServiceAvailable != ConnectionResult.SUCCESS) {
            Log.d("o3nWatcherLog", "Google Play service is not available (status=" + result + ")");
        }
        else {
            locationClient = new LocationClient(context, this, this);
            locationClient.connect();
        }
    }
}

    // This method is only used in a log line to have readable status in logs
private String getType(int type){
    if(type == DetectedActivity.UNKNOWN)
        return "UNKNOWN";
    else if(type == DetectedActivity.IN_VEHICLE)
        return "IN_VEHICLE";
    else if(type == DetectedActivity.ON_BICYCLE)
        return "ON_BICYCLE";
    else if(type == DetectedActivity.ON_FOOT)
        return "ON_FOOT";
    else if(type == DetectedActivity.STILL)
        return "STILL";
    else if(type == DetectedActivity.TILTING)
        return "TILTING";
    else
        return "";
}


private void evaluateActivityResult() {
    // (Based on previousActivityCode and current activityCode 
            // assign a value to activityEvaluation)
    // compare activityCode to previousActivityCode
    activityEvaluation = ...;

    previousActivityCode = activityCode;
}

private void actOnEvaluation(Location loc) {

    // Based on activityEvaluation decide to post or not

    if ( activityEvaluation ....) 
        prepareTheLocationJsonAndRecord(loc);
}

private void prepareTheLocationJsonAndRecord(Location loc) {
    // Record the location
}

@Override
public void onConnectionFailed(ConnectionResult arg0) {
    //Toast.makeText(context, "Google location services connection failed", Toast.LENGTH_LONG).show();
    Log.d("o3nWatcherLog","Google location services connection failed");
}

@Override
public void onDisconnected() {
    //Toast.makeText(context, "Google location services disconnected", Toast.LENGTH_LONG).show();
    Log.d("o3nWatcherLog", "Google location services disconnected");
}

@Override
public void onConnected(Bundle arg0) {
    //Toast.makeText(context, "Google location services connected", Toast.LENGTH_LONG).show();
    Log.d("o3nWatcherLog", "Google location services connected");

    Location loc = locationClient.getLastLocation();
    Log.d("o3nWatcherLog", "location= " + loc.toString());

    if (loc!=null)
        actOnEvaluation(loc);
}

}
umutozkan
  • 21
  • 1
  • 3

1 Answers1

0

I assume you need to set up some Power Wake lock to make phone to process GPS locations even in "sleep" mode. Like WiFi lock, etc. When I did something like that I used:

    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
    if (pm!=null){
        pmWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK+PowerManager.ON_AFTER_RELEASE, APP_TAG);
        pmWakeLock.acquire();
    }

But that was inside of a GPS-tracking activity. You need to alter params of newWakeLock for your needs. Maybe the service you are using has some code about it so you just need to declare using of WakeLock permission in the Manifest first.

Stan
  • 6,511
  • 8
  • 55
  • 87
  • I tried that at some point. That's why there is the WAKE_LOCK permission is in manifest file. I thought activity recognition is supposed to wake the phone though. Quoting "The activities are detected by periodically waking up the device and reading short bursts of sensor data. It only makes use of low power sensors in order to keep the power usage to a minimum." from http://developer.android.com/reference/com/google/android/gms/location/ActivityRecognitionClient.html – umutozkan Apr 12 '14 at 19:32
  • Looks like an AOS dilemma: need to prevent fast battery draining but also has to do some job in bg (I'm sure you understand that GPS "eats" battery and thats why it disabled in sleep)... So maybe you need an activity showing a map or somethig just to keep phone awake with something like my code while you want to track GPS or agree with GPS shutdown while phone sleeps. At least give it a try. – Stan Apr 12 '14 at 19:37
  • Well google location services is supposed to be a low power consumption alternative to gps. (It creates a "fused" location from network information) That's why I am not directly accessing GPS. (previous version of this app was doing just that) Anyways, I need to periodically record the users location even when user is not interacting with the phone. So it needs to be in the background, hence I need to solve this sleep issue. – umutozkan Apr 12 '14 at 19:44
  • well if its about "network" locations instead of GPS then probably PM lock on WiFi should do the trick – Stan Apr 12 '14 at 19:46
  • Can you elaborate? How to do the PM lock on WiFi? – umutozkan Apr 12 '14 at 19:49
  • http://developer.android.com/reference/android/net/wifi/WifiManager.WifiLock.html ? – Stan Apr 12 '14 at 19:59