3

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>
turtleboy
  • 8,210
  • 27
  • 100
  • 199
  • "and actually shouldn't be" -- you are not going to have much of an option. Your current approach is a hack to get around having a foreground service; it is not surprising that Google closed that hole. You could also look into `WorkManager`. – CommonsWare Jul 05 '21 at 17:40
  • @CommonsWare hi Mark, thanks for the input, I'll check out WorkManager – turtleboy Jul 05 '21 at 19:04
  • @CommonsWare Mark, is there an alternative to WorkManager? i've had a quick look and it will involve me converting my project to use AndroidX i think. I've tested all code and business demands mean i haven't much time before having to release this next version to my customers. The solution need to reach my customer base in the next day or 2. They have already signed a contact for the corporate phnes. i will implement work manager on the next version but i am just wondering if there is a quick alternative to buy me some time? – turtleboy Jul 05 '21 at 19:16
  • "if there is a quick alternative to buy me some time?" -- use a foreground service. Or, use the `JobScheduler` API directly. – CommonsWare Jul 05 '21 at 19:19
  • @CommonsWare I've just quickly created a worker class after updating Gradle to import the library. The IDE seems to recognise the worker class, so all could be well. Thanks – turtleboy Jul 05 '21 at 19:28

0 Answers0