I have created an Enterprise mobility managament (EMM)app. When i launched it a few years ago my customer base probably had Android < 8 devices. I've recently had to update my app and the customer base are now using Android 11 devices.
The problem:
I have a BG service that constantly runs. It counts down from 500, then when it hits 0, it fires off a web service that updates states in the EMM portal. The portal is what the customers use to see their devices out in the field.
In the ondestroy method of my BG service, i send a broadcast that uses a receiver to relaunch the destroyed service, thus keeping it alive.
My customers need this service alive as it sends info like data usage and battery level info which could be safety critical in their industry.
What i have tried:
I understand that Android Doze puts app to sleep, i have tried using my solution to send a command to the device that whitelists it from Doze, this does not work.
I've tried using JobIntentService instead but this did not work either.
NB. my app is a device admin app, so does not need to be in the foregroud and actually shouldn't be. Device Admins are also exemp from Doze apparently.
The error:
Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=xxxxx/.ConnectivityService }: app is in background
What is the best way to keep this service running in the background on Android 11?
here is my code:
<service
android:name="xxx.ConnectivityService"
android:enabled="true"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
.
public class ConnectivityServiceRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.e(ConnectivityServiceRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! ");
context.startService(new Intent(context, ConnectivityService.class));
//ConnectivityService.enqueueWork(context, new Intent());
}
}
.
public class ConnectivityService extends Service {
private static final String TAG = ConnectivityService.class.getSimpleName();
public int counter=0;
AppObj appobj;
public ConnectivityService(Context applicationContext) {
super();
Log.e(TAG, "inside ConnectivityService!");
}
public ConnectivityService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Log.e(TAG, "inside onStartCommand");
appobj = (AppObj)getApplication();
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "ondestroy!");
Intent broadcastIntent = new Intent("xxx.ActivityRecognition.RestartConnectivityservice");
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
Log.e(TAG, "inside startTimer");
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
Log.e(TAG, "inside initializeTimerTask");
timerTask = new TimerTask() {
public void run() {
Log.e(TAG, "in timer ++++ "+ (counter++));
if(counter == 3600){
counter = 0;
appobj.webService.sendPulseToServer();
Intent myIntent = new Intent(getApplicationContext(), TrafficMonitorService.class);
myIntent.setAction("xxx.TrafficMonitorService");
getApplicationContext().startService(myIntent);
}
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
.
<receiver
android:name="xxx.ConnectivityServiceRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="ConnectivityServiceRestartServiceWhenStopped">
<intent-filter>
<action android:name="xxx.ActivityRecognition.RestartConnectivityservice"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Related
I'm writing a service that sends a message to a remote server every interval.
I'm using Android 4.4 (KitKat).
The service also has an activity that allows some minimal configuration such as the remote server's URL.
I managed to make the service work, start from boot and restart if killed, but I have some issue with the latter.
If I open the app for the first time, all works well. Killing the app results in a broadcast intent and the service revives (without the activity of course). Now, if I open the app a second time (after the kill and revive), the next time I kill the app, the service does not restart.
My broadcast receiver:
public class dynDNSBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "dynDNSBroadcastReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"Broadcast received, starting dynDNS service");
context.startService(new Intent(context, dyndns.class));
}
}
My service:
public class dyndns extends Service {
private static String TAG = "dynDNS service";
public SharedPreferences myPrefs;
private Handler mainHandler;
private Timer timer;
#Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "Service onBind");
return null;
}
#Override
public void onCreate() {
super.onCreate();
startForeground(1, new Notification());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "dynDNS service started");
mainHandler = new Handler(getApplicationContext().getMainLooper());
timer = new Timer();
timer.schedule(new MyTimerTask(), 10000, 10000);
return START_STICKY;
}
#Override
public void onDestroy() {
timer.cancel();
super.onDestroy();
Log.d(TAG, "dynDNS service destroyed");
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("restartService");
broadcastIntent.setClass(this, dynDNSBroadcastReceiver.class);
this.sendBroadcast(broadcastIntent);
}
Relevant code from my activity:
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
assert manager != null;
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.d(TAG, serviceClass.getName());
Log.d (TAG, "Service status: Running");
return true;
}
}
Log.d (TAG, "Service status: Not running");
return false;
}
#Override
protected void onDestroy() {
stopService(mServiceIntent);
super.onDestroy();
}
Relevant code form the manifest:
<service
android:name="com.dyndns.dyndns"
android:enabled="true" android:process=":externalProcess">
<intent-filter>
<action android:name="com.dyndns.dyndns" />
</intent-filter>
</service>
<receiver android:name="dynDNSBroadcastReceiver" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="restartService"/>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
I have created a background service for registering SCREEN_ON and SCREEN_OFF intent to catch an event using broadcast receiver but some time due to unknown reason my service does not pass intent to my main application. For testing I have generated log file using ten second timer that it is my background service working or not, which I have attached below. I got a issue that my background service pause some time and start it automatically.
It is my background service
public class MyBackgroundService extends Service {
BroadcastReceiver receiver;
private static final String LOG_TAG = "MyBackgroundService";
#Override
public void onCreate() {
super.onCreate();
Timber.i("Foreground Service OnCreate");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Timber.i("Start Foreground Service");
receiver = new ScreenStateReceiver();
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
if (intent.getAction().equals(Constants.STARTFOREGROUND_ACTION)) {
Intent notificationIntent = new Intent(this, HomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("text")
.setTicker("text")
.setContentText("text")
.setSmallIcon(R.drawable.logo_icon)
.setContentIntent(pendingIntent)
.setOngoing(true).build();
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE,
notification);
}
startTimer();
return START_STICKY;
}
#Override
public boolean onUnbind(Intent intent) {
Timber.i("Foreground Service onUnbind");
return super.onUnbind(intent);
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Timber.i("Foreground Service onTaskRemoved");
super.onTaskRemoved(rootIntent);
}
#Override
public void onDestroy() {
stopForeground(true);
Timber.i("Foreground Service onDestroy");
stoptimertask();
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// Used only in case of bound services.
return null;
}
private Timer timer;
private TimerTask timerTask;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 10000, 10000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Timber.i("Timer");
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
}
It is my broadcast Receiver
public class ScreenStateReceiver extends BroadcastReceiver {
public ScreenStateReceiver(){
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(Intent.ACTION_SCREEN_OFF)){
Timber.i("Screen OFF");
}
else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
Timber.i("Screen ON");
}
}
}
Menifest,xml file declaration
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<service android:name=".service.MyBackgroundService"/>
<receiver android:name=".receiver.ScreenStateReceiver" android:enabled="true" android:exported="true">
<intent-filter >
<action android:name="android.intent.action.SCREEN_OFF" />
<action android:name="android.intent.action.SCREEN_ON" />
</intent-filter>
</receiver>
I am starting Service using intent
Intent service = new Intent(getContext().getApplicationContext(), MyBackgroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
service.setAction(Constants.STARTFOREGROUND_ACTION);
getContext().getApplicationContext().startForegroundService(service);
}
else{
service.setAction(Constants.MAIN_ACTION);
getContext().getApplicationContext().startService(service);
}
Here this picture of my log file
LOG FILE
using this log file you can see background service is stop and start automatically.
please help me resolve this issue.
There is a restriction for service in Oreo onward, We can start a service by calling startService() only when the application is in foreground. When you exit from the app (app is in background), then it has a window of several minute, in that app can run the service. At the end of window, app considered to be idle,that time system stop the background service. In order to achieve run a service even in background, either we have to implement foreground service or we have to use job scheduler etc.
I'm creating a service which should work when the activity is in background as well as when the whole application is destroyed.
I'm calling location coordinates in the service at every 1 minute interval.
However, when I try to do so, the service shuts down automatically just after 12-15 minutes.
I want the service to work endlessly until and unless it is destroyed by the completion of activity on user interaction.
My service class is as follows:
public class SensorService extends Service {
public int counter=0;
public static final int NINTY_SECONDS = 90000;
public static Boolean isRunning = false;
public LocationManager mLocationManager;
public Get_Coordinates mLocationListener;
public GetSharedPreference sharedPreference;
#Override
public void onCreate() {
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mLocationListener = new Get_Coordinates(getApplicationContext());
sharedPreference = new GetSharedPreference(getApplicationContext());
startTimer();
super.onCreate();
}
public SensorService(Context applicationContext) {
super();
Log.i("HERE", "here I am!");
}
public SensorService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i("EXIT", "ondestroy!");
Intent broadcastIntent = new Intent("com.android.startBgService");
broadcastIntent.putExtra("abc","abcd");
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, NINTY_SECONDS, NINTY_SECONDS); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
// Log.i("in Etro Sendor", "in timer ++++ "+ (counter++));
if (Check_Internet_Con.isConnectingToInternet(getApplicationContext())) {
if (!isRunning) {
startListening();
}
try {
if (sharedPreference.getActiveUserId() > 0) {
mLocationListener.getLocation();
mLocationListener.insertCoordinatesInSqlite();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
public void startListening() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (mLocationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER))
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, mLocationListener, Looper.getMainLooper());
if (mLocationManager.getAllProviders().contains(LocationManager.GPS_PROVIDER))
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListener,Looper.getMainLooper());
}
isRunning = true;
}
}
Here is my manifest
<service
android:name="com.lunetta.etro.e_tro.SensorService"
android:enabled="true"></service>
<service
android:name="com.lunetta.etro.e_tro.SecondService"
android:enabled="true" >
</service>
<receiver
android:name=".SensorRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="RestartServiceWhenStopped">
<intent-filter>
<action android:name="com.android.startBgService" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
And Here is SensorRestarterBroadcastReceiver class
public class SensorRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(SensorRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! Oooooooooooooppppssssss!!!!");
context.startService(new Intent(context, SensorService.class));
}
}
https://developer.android.com/guide/components/bound-services.html
"A bound service typically lives only while it serves another application component and does not run in the background indefinitely."
To make it run indefinitely, you need to bind the service to a UI component that exists indefinitely. You can use an Android Notification. This is a foreground service.
https://developer.android.com/guide/components/services.html#Foreground
Yes it will stop working, check out the new Docs put up by Google, It has changed completely how location works for battery performance, and yes it having a big deal effect on performance i have changed my completely for the very specific task. Also stop using other repos that do claim that they work are wrong
Check the new updated docs by google
https://developer.android.com/training/building-location.html
The best way I implemented your same requirement is and is live in over about 60k devices and working flawlessly is With depending on Version of Android API using JobService with 23 and higher and lower with Background service. Have use LocationProvider API and Location provider Client API accordingly.
with personal experience i will say this The old style of code use to drain the device battery in few hours, now my code is hardly making a dent, its consuming only like 15 percent overnight overall usage. thats a big change in consumption.
How to run long running service in android in all devices greater than api level 16. I have written below code for the service but somehow not working for api 17 - 19. and its working for all devices above 19 api level. Please have a look and suggest me how to do it. I am new android and writing service for the first time.
below is code
public class MyService extends Service {
public int counter = 0;
public ViksitService(Context applicationContext) {
super();
Log.i("HERE", "here I am!");
}
public MyService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i("EXIT", "ondestroy!");
Intent broadcastIntent = new Intent("MyService_RESTART_SERVICE");
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
long oldTime = 0;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Log.i("in timer", "in timer ++++ " + (counter++));
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
below is broadcast reciever :
public class ServiceRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(ServiceRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! Oooooooooooooppppssssss!!!!");
context.startService(new Intent(context, MyService.class));;
}
}
below is manifest :
<service
android:name=".services.MyService"
android:enabled="true"
android:exported="true">
</service>
<receiver
android:name=".services.ServiceRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="MyService_RESTART_SERVICE"/>
</intent-filter>
</receiver>
I know that is a well known subject, but I have tried lot of things. I have an simple application, dedicated to a specific user, application has an mainActivity which is displaying some status on screen and it's starting two services, one is making request from a server (at every 5 minutes) and one which is sending sms and replay to server (at every ten minutes).
The application is running on a Samsung pocket 2 with Android 4.4.2, this device is used only for this application. While the device is connected to ADB the services are working just fine, but if I disconnect the phone and let it running normally, the services are killed repeatable and restarted after a while. The messaged are send with very much delay. I would be thankful for any suggestions.
Here is my code:
Main activity:
public class MainActivity extends Activity {
private TextView _internet;
private TextView _signal;
private TextView _server;
private BroadcastReceiver receiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
IntentFilter intentFilter = new IntentFilter(Constants.SS);
receiverWorker();
registerReceiver(receiver, intentFilter);
startService(new Intent(this, RefreshDBService.class));
startService(new Intent(this, SmsService.class));
}
private void receiverWorker() {
receiver = new BroadcastReceiver() {
public void onReceive(Context arg0, Intent arg1) {
checkState();
}};
}
public void refreshButonClicked(View v) {
checkState();
}`
Here is my first service:
public class RefreshDBService extends Service {
private Thread _backgroundWork;
private ScheduledExecutorService scheduleTaskExecutor = Executors.newScheduledThreadPool(1);
private DataBaseOperations _dataSource;
#Override
public void onCreate() {
super.onCreate();
_dataSource = new DataBaseOperations(this);
_backgroundWork = new Thread(new Runnable() {
#Override
public void run() {
if(Checks.checkInternetConnection(getApplicationContext())){
if(ServerOperations.isServerAvailable(getApplicationContext())){
String inputData = ServerOperations.makeRequest(Constants.GET_DATA_ROUTE, ServerOperations.getMessagesFromServer(getApplicationContext()));
ArrayList<DataSmsObj> dataFromServer=null;
if(inputData!=null && !inputData.isEmpty()){
dataFromServer = ServerOperations.fromJsonToObjects(inputData);
if(dataFromServer.size()>0){
_dataSource.open();
_dataSource.insertDataFromServer(dataFromServer);
_dataSource.close();
}
}
System.out.println("check server for messages in pending status, received -> "+ dataFromServer.size());
}else{
System.out.println("no server");
sentErrorToUI(Constants.NO_SERVER);
}
}else{
System.out.println("no internet");
sentErrorToUI(Constants.NO_INTERNET);
}
}
});
}
public int onStartCommand(Intent intent, int flags, int startId) {
scheduleTaskExecutor.scheduleWithFixedDelay(_backgroundWork, 0, Constants.NEXT_CYCLE/2, TimeUnit.MINUTES);
return START_REDELIVER_INTENT;
}
#Override
public void onDestroy() {
super.onDestroy();
scheduleTaskExecutor.shutdownNow();
}
public IBinder onBind(Intent intent) {
return null;
}
private void sentErrorToUI(String message){
Intent intent = new Intent(Constants.SS);
intent.putExtra(Constants.SS, message);
System.out.println("trimit" +message);
sendBroadcast(intent);
}
}
And this is the second one:
public class SmsService extends Service {
private Thread _backgroundWork;
private ScheduledExecutorService scheduleTaskExecutor = Executors.newScheduledThreadPool(1);
private DataBaseOperations _dataSource;
#Override
public void onCreate() {
super.onCreate();
_dataSource = new DataBaseOperations(this);
_backgroundWork = new Thread(new Runnable() {
#Override
public void run() {
sendFeedbackToServer();
List<DataSmsObj> dataToSent = new ArrayList<DataSmsObj>();
_dataSource.open();
dataToSent = _dataSource.getDataToSent();
_dataSource.close();
System.out.println("messages to sent: "+ dataToSent.size());
for (int i = 0; i < dataToSent.size(); i++) {
//here the messages are send, the code is to long to put it here, but if is need i can do it afterwards
}
}
});
}
public int onStartCommand(Intent intent, int flags, int startId) {
scheduleTaskExecutor.scheduleWithFixedDelay(_backgroundWork, 0, Constants.NEXT_CYCLE, TimeUnit.MINUTES);
return START_REDELIVER_INTENT;
}
#Override
public void onDestroy() {
super.onDestroy();
scheduleTaskExecutor.shutdownNow();
public IBinder onBind(Intent intent) {
return null;
}
If you are using a background Service with a scheduled task, it could be killed by the system. The only way to prevent the killing is a foreground Service. Quoting the documentation:
A foreground service is a service that the user is actively aware of and is not a candidate for the system to kill when low on memory.
You have to call the method startForeground() inside your Service using a Notification to show it. For further information you can check: https://developer.android.com/guide/components/services.html#Foreground
By the way, I recommend you to use the new JobScheduler api above api 21.
https://developer.android.com/reference/android/app/job/JobScheduler.html
Android kills service based on priority stack.
Android: keeping a background service alive (preventing process death)
What is START_STICKY,START_NOT_STICKY and START_REDELIVER_INTENT Service
Above links might help you.
Your devices will sleeps if it is unplugged from computer . So, the solutions :
Use startForeground method to prevent service to be killed and/or use AlarmManager in order to charge event.
It is possible to use start_stiky flag but it just restarts the process if it killed by system.