FileObserver does not triggering events while invoking in background service - android

There are answered questions regarding FileObserver in Android and I am following them but still my code doesn't work. Here I am posting my code, I am trying to set fileObserver via service so it work even if the app itself is closed. When running, it is invoking the DirectoryObserver Constructor but adding or deleting a file doesn't invoke the event
public class MainActivity extends AppCompatActivity
{
private String sharedPreferencesKey = "IsThisFIrstTime";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
if (!preferences.contains(sharedPreferencesKey)) {
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(sharedPreferencesKey, false);
editor.apply();
try {
startTheServices();
}
catch (Exception ex) {
}
}
setContentView(R.layout.activity_main);
}
private void startTheServices()
{
Intent intent = new Intent(this, BackgroundServices.class);
startService(intent);
}
}
public class BackgroundServices extends Service {
#Override
public void onCreate(){
super.onCreate();
Toast.makeText(this, "This is on Create", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "This is on onStartCommand", Toast.LENGTH_LONG).show();
Thread thread = new Thread(new ThreadClass(startId));
thread.start();
return super.onStartCommand(intent, flags, startId);
//return START_STICKY;
}
#Override
public void onDestroy(){
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
final class ThreadClass implements Runnable {
int _serviceId;
ThreadClass(int serviceId) {
_serviceId = serviceId;
}
#Override
public void run() {
DirectoryObserver directoryObserver = new DirectoryObserver(new File(Environment.getExternalStorageDirectory(), Constants.Camera_Directory).getAbsolutePath(), getApplicationContext());
directoryObserver.startWatching();
}
}
}
public class DirectoryObserver extends FileObserver {
private static final String TAG = "DIRECTORY_OBERSVER";
private String directoryPath;
private Context _context;
public DirectoryObserver(String path, Context context) {
super(path);
Log.i(TAG, "Something Happening " + path);
_context = context;
directoryPath = path;
}
#Override
public void onEvent(int event, #Nullable String path) {
if (path == null) {
return;
}
//a new file or subdirectory was created under the monitored directory
if ((FileObserver.CREATE & event)!=0) {
Log.i(TAG, "A file is added to the path " + path);
Toast.makeText(_context, "A new file has been added", Toast.LENGTH_LONG).show();
}
if ((FileObserver.DELETE & event)!=0) {
Log.i(TAG, "A file is deleted to the path " + path);
//Context.getApplicationContext();
}
}
}
And following is the menifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="someone.package">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".BackgroundServices" android:exported="false"></service>
</application>
</manifest>

Have You Added The Permission? I Don't Have Enough Reps Otherwise I Would have Commented.

The problem here is that the FileObserver is probably being garbage collected, as you can see here:
Warning: If a FileObserver is garbage collected, it will stop sending events. To ensure you keep receiving events, you must keep a reference to the FileObserver instance from some other live object.
Android might be getting rid of your service, or even the FileObserver itself. Try to see if the code is entering the "startWatching()" method, or even if the service is starting.

The solution I found is that move the following initialization of DirectoryObserver
DirectoryObserver directoryObserver = new DirectoryObserver(new File(Environment.getExternalStorageDirectory(), Constants.Camera_Directory).getAbsolutePath(), getApplicationContext());
to
public int onStartCommand(Intent intent, int flags, int startId)
method in BackgroundService Class before following lines
Thread thread = new Thread(new ThreadClass(startId));
thread.start();

Related

Unable to launch app when removed from recent apps when foreground service is running Android

I have implemented a foreground service with a notification for my existing android project.
Problem: I remove the app from the "recent app" when the foreground service is running and its notification is visible. Later, I try to launch the app by clicking on the app icon. A black screen appears for a second and the app is not launched.
The app is stuck in this state until I got to app settings and force close the application.
Foreground Service:
public class FileSyncService extends Service {
private static final int FILE_SYNC_SERVICE_ID = 901;
public static final String FILE_SYNC_SERVICE_ACTION_STOP = "StopFileSyncService";
public static final String FILE_SYNC_SERVICE_ACTION_START = "StartFileSyncService";
#Inject
private FilePushManager filePushManager;
#Inject
private NotificationController notificationController;
#Override
public void onCreate() {
super.onCreate();
Logger.d("[FileSyncService][onCreate]");
BootstrappingManager.getInstance().getInjector().injectMembers(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Logger.d("[FileSyncService][onStartCommand]");
if (intent != null && intent.getAction() != null && intent.getAction().equals(FILE_SYNC_SERVICE_ACTION_STOP)) {
Logger.d("[FileSyncService][onStartCommand] stopSelf");
stopForegroundService();
} else {
Logger.d("[FileSyncService][onStartCommand] startForeground");
startForeground(FILE_SYNC_SERVICE_ID, notificationController.createFileSyncNotification());
filePushManager.startFilePush();
}
return START_STICKY;
}
#Override
public void onDestroy() {
Logger.d("[FileSyncService] onDestroy");
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void stopForegroundService() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_REMOVE);
} else {
stopForeground(true);
}
stopSelf();
}
}
Methods to start stop service:
public static void startFileSyncService(Context context) {
Intent serviceIntent = new Intent(context, FileSyncService.class);
serviceIntent.setAction(FileSyncService.FILE_SYNC_SERVICE_ACTION_START);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Logger.d("[MandatoryUtils][startFileSyncService] foreground");
try {
context.startForegroundService(serviceIntent);
} catch (SecurityException e) {
Logger.e("[MandatoryUtils][startFileSyncService] unable to start File Sync Service", e);
} catch (Exception e) {
Logger.e("[MandatoryUtils][startFileSyncService] unable to start File Sync Service Exception", e);
}
} else {
Logger.d("[MandatoryUtils][startFileSyncService] ");
context.startService(serviceIntent);
}
}
public static void stopFileSyncService(Context context) {
Logger.d("[MandatoryUtils][stopFileSyncService]");
Intent serviceIntent = new Intent(context, FileSyncService.class);
serviceIntent.setAction(FileSyncService.FILE_SYNC_SERVICE_ACTION_STOP);
context.startService(serviceIntent);
}
Launcher Activity:
<application
android:name="net.activities.MainApplication"
android:allowBackup="false"
android:icon="#drawable/scl"
android:label="#string/app_name"
android:manageSpaceActivity="net.activities.SplashActivity"
android:networkSecurityConfig="#xml/network_security_config"
android:resizeableActivity="false"
android:theme="#style/ContentLibraryTheme"
tools:replace="allowBackup, android:theme">
<uses-library android:name="org.apache.http.legacy" android:required="false" />
<activity
android:name="net.activities.SplashActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true"
android:theme="#style/Theme.App.Splash"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
...

Bind to AIDL remote service on Android

I have an Android app that specifies two AIDL files and a service. This service should be used from another app to invoke the methods defined on the AIDL. I have followed the Android Documentation on AIDL to implement the AIDL files and the service (see the code below).
Then I created a very simple client app (also shown below) to bind to the service and invoke the method defined on my AIDL file. However, the bindService always returns false and mentions that the intent cannot be found. These are some things I tried to correctly reference the intent on the client side:
Intent intent = new Intent("a.b.c.service");
intent.setPackage("a.b.c");
---
Intent intent = new Intent("service");
intent.setPackage("a.b.c");
---
Intent intent = new Intent();
intent.setClassName("a.b.c", "a.b.c.services.MyService");
---
Intent intent = new Intent();
intent.setClassName("a.b.c.services", "a.b.c.services.MyService");
---
Intent intent = new Intent();
intent.setClassName("a.b.c", ".services.MyService");
---
Intent intent = new Intent();
intent.setAction("service");
intent.setPackage("a.b.c");
intent.setClassName("a.b.c", ".services.MyService");
---
Intent intent = new Intent();
intent.setAction("service");
intent.setClassName("a.b.c", ".services.MyService");
If I try from the same application where the service resides, I can use the following and it will work:
Intent intent = new Intent(this, MyService.class);
But since this is a remote service, I do not have access to MyService class from the client app, so I can't find any way of making it work.
I have wondered through a lot of StackOverflow posts without any luck. Examples:
Android: Binding to a remote service
How can I use AIDL remote service to deal with defferent clients' concurrent requests?
Android Bind Service returns false every time
How should I specify my intent in this case?
Thanks in advance.
Relevant code:
IServiceInterface.aidl
package a.b.c;
import a.b.c.IServiceInterfaceGetStuffCallback;
interface IServiceInterface
{
void getStuff(String arg1, IServiceInterfaceGetStuffCallback callback);
}
IServiceInterfaceGetStuffCallback
package a.b.c;
interface IServiceInterfaceGetStuffCallback
{
void onGetStuffResponse(String arg1, boolean arg2, int arg3, int arg4);
}
a.b.c./services/MyService.java
public class MyService extends Service
{
private final MyService self = this;
private MyServiceHandler handler = null;
private final HandlerThread handlerThread = new HandlerThread("AidlServiceThread");
//Callbacks
private final ArrayList<IServiceInterfaceGetStuffCallback> getStuffCallbacks = new ArrayList<>();
private final int MY_SERVICE_GET_STUFF_MSG = 1;
public MyService()
{
}
#Override
public IBinder onBind(Intent intent)
{
// Handler Thread handling all callback methods
handlerThread.start();
handler = new MyServiceHandler(handlerThread.getLooper());
return mBinder;
}
IServiceInterface.Stub mBinder = new IServiceInterface.Stub()
{
#Override
public void getStuff(String arg1, IServiceInterfaceGetStuffCallback callback) throws RemoteException
{
//Register the callback internally
getStuffCallbacks.add(callback);
final int cbIndex = getStuffCallbacks.size() - 1;
getStuff((arg1, arg2, arg3, arg4) ->
{
MyServiceResponse response = new MyServiceResponse();
response.arg1 = arg1;
response.arg2 = arg2;
response.arg3 = arg3;
response.arg4 = arg4;
Message message = handler.obtainMessage();
message.arg1 = cbIndex;
message.obj = response;
message.what = MY_SERVICE_GET_STUFF_MSG;
handler.sendMessage(message);
});
}
};
private class MyServiceHandler extends Handler
{
int callbackIndex = 0;
MyServiceHandler (Looper looper)
{
super(looper);
}
#Override
public void handleMessage(Message msg)
{
callbackIndex = msg.arg1;
MyServiceHandler response = (MyServiceHandler)msg.obj;
switch (msg.what)
{
case MY_SERVICE_GET_STUFF_MSG:
{
try
{
getStuffCallbacks.get(callbackIndex).onGetStuffResponse(response.arg1, response.arg2, response.arg3, response.arg4);
}
catch (RemoteException e)
{
e.printStackTrace();
}
break;
}
default:
break;
}
}
}
private static class MyServiceResponse
{
public String arg1;
public boolean arg2;
public int arg3;
public int arg4;
}
}
Android Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="a.b.c">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<permission
android:name="a.b.c.myservice"
android:protectionLevel="signature" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|keyboard|colorMode|density|navigation|fontScale|layoutDirection|locale|mcc|mnc|smallestScreenSize|touchscreen|uiMode">
(...)
<service
android:name="a.b.c.services.MyService"
android:enabled="true"
android:exported="true"
android:permission="a.b.c.myservice">
<intent-filter>
<action android:name="a.b.c.myservice" />
</intent-filter>
</service>
</application>
</manifest>
Client app - MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
private final String TAG = "aidltest";
MainActivity self = this;
IServiceInterface service = null;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_get_stuff).setOnClickListener(this);
}
#Override
public void onClick(View view)
{
if (view.getId() == R.id.btn_get_stuff)
getStuff();
}
void getStuff()
{
Log.e(TAG, "getStuff invoked");
Intent intent = new Intent("a.b.c.myservice");
intent.setPackage("a.b.c");
boolean res = getApplicationContext().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
Log.e(TAG, "Service binding result: " + res);
}
private ServiceConnection serviceConnection = new ServiceConnection()
{
public void onServiceConnected(ComponentName className, IBinder service)
{
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
self.service = IServiceInterface.Stub.asInterface(service);
Log.e(TAG, "ServiceInterface attached");
}
public void onServiceDisconnected(ComponentName className)
{
service = null;
Log.e(TAG, "Service disconnected");
}
};
}
The following changes work for me:
Adjust your manifest as follows:
<service
android:name="a.b.c.services.MyService"
android:enabled="true"
android:exported="true"
android:permission="a.b.c.myservice">
<intent-filter>
<action android:name="a.b.c.myservice" />
<category android:name="android.intent.category.DEFAULT"/> <---- NEW LINE
</intent-filter>
</service>
Run adb shell pm list packages and get the package id of the apk where you declared the service. This is needed for building the intent in step 3. Let's call it PACKAGE_ID.
Adjust the getStuff method as follows:
void getStuff() {
Log.e(TAG, "getStuff invoked");
Intent intent = new Intent("a.b.c.myservice"); // This is the value you used in the action for your service as declared in the manifest.
intent.setPackage(PACKAGE_ID); // This is the value you retrieved in step 2.
boolean res = getApplicationContext().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
Log.e(TAG, "Service binding result: " + res);
}

AIDL service not connecting after bindService()

I am trying to develop a setup of 2 applications (service app + client app) using AIDL. I have currently a setup of 3 modules:
android-agent-framework (android library module holding only the AIDL file)
android-agent (the service)
android-example-client (the client)
android-agent and android-agent-framework have a dependency to the first one to get access to the interface.
Whenever the client calls bindService() it gets false as return and in the ServiceConnection the onServiceConnected() is not called. Also in the service implementation the onBind() is not called. There is no error in the logs.
Here is the code:
android-agent activity:
public class MyCompanyStartActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(MyCompanyStartActivity.class.toString(), "Create MyCompanyStartActivity");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ComponentName service = startService(new Intent(this, MyCompanyRequestService.class));
Log.i("tag", service.getClassName() + "::" + service.getPackageName());
}
}
android-agent service:
public class MyCompanyRequestService extends Service {
#Override
public IBinder onBind(Intent intent) {
Log.i(MyCompanyRequestService.class.toString(), "Starting SmartRest Service");
return mBinder;
}
private final IMyCompanyRequestService.Stub mBinder = new IMyCompanyRequestService.Stub() {
#Override
public void sendData(String xid, String authentication, String data) throws RemoteException{
Log.i(MyCompanyRequestService.class.toString(), "sending data: " + data);
}
};
}
android-agent manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mycompany.android.agent" >
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MyCompanyStartActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Services -->
<service
android:name="com.mycompany.android.agent.framework.MyCompanyRequestService"
android:process=":remote"
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="MyCompanyRequestService"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
<!-- Permissions -->
</application>
</manifest>
android-example-client activity:
public class ClientStarter extends Activity {
protected IMyCompanyRequestService mycompanyRequestService = null;
#Override
public void onCreate(Bundle savedInstanceState) {
Log.i("tag","create client");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
if (mycompanyRequestService == null) {
printServices();
Intent it = new Intent("MyCompanyRequestService");
it.setPackage("com.mycompany.android.agent.framework");
Log.i("tag","before binding service: " + it.getAction() + "::" + it.getPackage());
boolean serviceBinding = getApplicationContext().bindService(it, connection, Context.BIND_AUTO_CREATE);
Log.i("tag", "service is bound: " + serviceBinding);
}
Handler handler = new Handler();
handler.postDelayed(new Runner(), 10000);
}
#Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("service", "Service connected");
mycompanyRequestService = IMyCompanyRequestService.Stub.asInterface(service);
Toast.makeText(getApplicationContext(), "Service Connected", Toast.LENGTH_SHORT).show();
Log.i("service", "Service connected");
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.i("service", "Service disconnected");
mycompanyRequestService = null;
Toast.makeText(getApplicationContext(), "Service Disconnected", Toast.LENGTH_SHORT).show();
Log.i("service", "Service disconnected");
}
};
private void printServices() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
Log.d("service", service.service.getClassName());
}
}
private class Runner implements Runnable {
#Override
public void run() {
Log.i("tag","starting");
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Location loc;
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Log.e(ClientStarter.class.toString(), "Error", e);
} while(true) {
try {
if (mycompanyRequestService != null) {
loc = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
Log.i(ClientStarter.class.toString(), loc.getLatitude() + " - " + loc.getLongitude() + " - " + loc.getAltitude());
mycompanyRequestService.sendData("test", "auth", String.valueOf(loc.getLatitude()) + "," + String.valueOf(loc.getLongitude()) + "," + String.valueOf(loc.getAltitude()));
} else {
Log.i(ClientStarter.class.toString(), "service not yet available");
}
Thread.sleep(5000);
} catch (InterruptedException e) {
Log.e(ClientStarter.class.toString(), "Error", e);
} catch (RemoteException e) {
Log.e(ClientStarter.class.toString(), "Error", e);
}
}
}
}
}
The printServices() call before trying to bind the service actually lists the service so it is running.
The log does not contain any errors and the client is in the end running in the loop but the service is still null.
Maybe someone encountered a similar issue before.
After going another round through all files I found my mistake.
I needed to change:
Intent it = new Intent("MyCompanyRequestService");
it.setPackage("com.mycompany.android.agent.framework");
to:
Intent it = new Intent("MyCompanyRequestService");
it.setPackage("com.mycompany.android.agent");
The package of the Intent needs to match the package of the app and not the package of the service.
Another reason why you could face this issue (at least I did) is that – from API level 30 – you are also required to declare the apps that you communicate to in the manifest, for example:
<queries>
<package android:name="com.your.app" />
</queries>

Application getting hanged when try to call camera class multiple times through service

I am trying to capture images through camera in background service but while doing this my application is getting hanged and some times it gives me error of " Fail to connect to camera service" Although my images are getting store after every 20 seconds by my app ui either get hanged or it crashes.
Please have a look on my service class :-
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
captureImage();
}
private void captureImage() {
int count = 0;
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (IS_ACTIVITY_FINISHED) {
count++;
if (count == 20) {
// Start Activity here
Intent translucent = new Intent(getApplicationContext(),
HiddenCamera.class);
translucent.putExtra("FLASH", "off");
translucent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(translucent);
IS_ACTIVITY_FINISHED = false;
break;
}
}
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
here is my HiddenCamera class :-
http://piratepad.net/ep/pad/view/ro.LgLDgdzewfJ/latest
my Manifest file :-
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<service android:name="com.example.services.CameraService" >
</service>
Please provide me any useful information regarding this process.
Thanks
Finally i am able to solve it :-
here is my service class code :-
private void startCapturingImage() {
Toast.makeText(getApplicationContext(), "InSide service class", 1000)
.show();
mDownTimer = new CountDownTimer(20000, 1000) {
#SuppressWarnings("deprecation")
#Override
public void onFinish() {
// count finished
if (IS_ACTIVITY_FINISHED) {
Toast.makeText(getApplicationContext(), "InSide on finished method class", 1000)
.show();
IS_ACTIVITY_FINISHED = false;
Intent translucent = new Intent(getApplicationContext(),
HiddenCamera.class);
translucent.putExtra("FLASH", "off");
translucent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(translucent);
mDownTimer.start();
}else{
startCapturingImage();
}
}
#Override
public void onTick(long millisUntilFinished) {
}
}.start();

ActivityRecognitionClient requestActivityUpdates() method doesn't trigger given PendingIntent when phone state is stand by

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

Categories

Resources