13

The app shows expected behavior if the app is running in the foreground, background or killed. However, once it is rebooted the PeriodicTask stops running

Following are the relevant bits of code:

In AndroidManifest:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<service android:name=".tracking.MyTaskService"
            android:exported="true"
            android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">
            <intent-filter>
                <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
            </intent-filter>
        </service>

PeriodicTask config:

PeriodicTask task = new PeriodicTask.Builder()
                .setService(MyTaskService.class)
                .setTag(TASK_TAG_PERIODIC)
                .setPeriod(30L)
                .setFlex(10L)
                .setExtras(bundle)
                .setPersisted(true)
                .build();

        mGcmNetworkManager.schedule(task);

In the Logcat, I get the following:

E/NetworkScheduler.TED: Couldn't start service: Intent 
{ act=com.google.android.gms.gcm.ACTION_TASK_READY
  cmp=xxx.xxxxxx.xxx/.tracking.MyTaskService (has extras) 
}

Appending all relevant details:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.example.gcmnetworkmanagerquickstart">

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        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>

        <!-- [START manifest_service] -->
        <service
            android:name=".MyTaskService"
            android:exported="true"
            android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">
            <intent-filter>
                <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
            </intent-filter>
        </service>
        <!-- [END manifest_service] -->

    </application>

</manifest>

MainActivity

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final int RC_PLAY_SERVICES = 123;
    public static final String TASK_TAG_PERIODIC = "periodic_task";

    private GcmNetworkManager mGcmNetworkManager;

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

        mGcmNetworkManager = GcmNetworkManager.getInstance(this);

        if(checkPlayServicesAvailable()){
            startPeriodicTask();
        }

    }



    public void startPeriodicTask() {
        Log.d(TAG, "startPeriodicTask");

        PeriodicTask task = new PeriodicTask.Builder()
                .setService(MyTaskService.class)
                .setTag(TASK_TAG_PERIODIC)
                .setPeriod(5)
                .setPersisted(true)
                .build();

        mGcmNetworkManager.schedule(task);
    }

    private boolean checkPlayServicesAvailable() {
        GoogleApiAvailability availability = GoogleApiAvailability.getInstance();
        int resultCode = availability.isGooglePlayServicesAvailable(this);

        if (resultCode != ConnectionResult.SUCCESS) {
            if (availability.isUserResolvableError(resultCode)) {
                // Show dialog to resolve the error.
                availability.getErrorDialog(this, resultCode, RC_PLAY_SERVICES).show();
            } else {
                // Unresolvable error
                Toast.makeText(this, "Google Play Services error", Toast.LENGTH_SHORT).show();
            }

            Log.d(TAG, "Play Services NOT Available");
            return false;
        } else {
            Log.d(TAG, "Play Services Available");
            return true;
        }
    }
}

MyTaskService

public class MyTaskService extends GcmTaskService {

    private static final String TAG = "MyTaskService";

    @Override
    public void onInitializeTasks() {
    }

    @Override
    public int onRunTask(TaskParams taskParams) {
        Log.d(TAG, "onRunTask: " + taskParams.getTag());

        return doPeriodicTask();
    }

    private int doPeriodicTask() {
        Log.d(TAG, "doPeriodicTask Called");
        return GcmNetworkManager.RESULT_SUCCESS;
    }


}

build.gradle (App Module)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.0"

    defaultConfig {
        applicationId "com.google.example.gcmnetworkmanagerquickstart"
        minSdkVersion 14
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

allprojects {
    repositories {
        jcenter()
        google()
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    implementation 'com.android.support:appcompat-v7:26.0.0-beta2'

    compile 'com.squareup.okhttp:okhttp:2.7.0'

    compile 'com.google.android.gms:play-services-gcm:11.0.2'
}

Edit1: After some days of analysis, I have figured out the following:

  1. This is a device specific issue. Doesn't happen on nexus devices, for example.
  2. This is part of a bigger issue. The devices showing this behavior also doesn't work as expected with AlarmManager, FirebaseJobScheduler and RECEIVE_BOOT_COMPLETED broadcast receiver.
  3. One workaround is this solution. However, this solution has at least 2 issues. (1) When you kill the app, AccessibilityService permission is reset. Which means everytime you open the app after this, manually permission is to be given. (2) If the app is killed, reboots after that won't hit the RECEIVE_BOOT_COMPLETED broadcast receiver
  4. Crazy finding: In one-plus devices, if your app has the word test in the package structure, everything works!!
  5. If you whitelist your application going to settings > Apps (The location and name of this could be different in different devices), everything works as expected.
  6. The start up apps to which you have to manually add your app contains well known apps such as WhatsApp, Facebook, Instagram and many others. When you install these apps, they get automatically added to this list! I'm yet to see a custom API published by any of these manufacturers for doing this. This makes me think that these apps are white listed from manufacturers' end.
ranjjose
  • 2,138
  • 1
  • 24
  • 46
  • While looking into the logs I stumbled upon the fact that google's own Youtube for Kids app also has the same issue: **E/NetworkScheduler.TED: Couldn't start service: Intent { act=com.google.android.gms.gcm.ACTION_TASK_READY cmp=com.google.android.apps.youtube.kids/com.google.android.libraries.youtube.common.gcore.gcoreclient.gcm.impl.GcmTaskServiceDelegator (has extras) }** – ranjjose Jul 16 '17 at 16:54
  • 1
    does it happen on multiple devices? – Pararth Jul 17 '17 at 06:35
  • Yes.. it does happen on multiple devices. I have experienced this on one plus, MI devices. – ranjjose Jul 17 '17 at 07:39
  • 1
    have you tried stock android devices? Nexus, Moto, Pixel etc ? some times it is the custom behavior(like MI devices force closing the app on 'killing' it) for OS implemented by OEMs, maybe you would need to opt for a workaround – Pararth Jul 17 '17 at 07:42
  • Hmm.. The whole thing is a bit strange. As part of implementing this feature, I have copied the code from https://github.com/googlesamples/android-gcmnetworkmanager and the app works as expected on the 1st project I made. When I copied the changes to our product, it stopped working. Even now, on that PoC, it still works. The problem is nowhere else it works!! – ranjjose Jul 17 '17 at 08:01
  • 1
    that takes the device/oem factor out then, becomes a case of comparing differences maybe.. like do you return the super for runTask or override it – Pararth Jul 17 '17 at 08:47
  • I haven't made any change to the GcmTaskService (subclass) of the demo. Only changes are concerning Gradle versions. And those are same in the working version as well as the not working version of the project!! – ranjjose Jul 17 '17 at 08:58
  • 1
    check manifest entries and service class too, not much i can think of right now – Pararth Jul 17 '17 at 09:06
  • 1
    OK, Thanks! Have checked all those places; I'll append all those details in the question for reference. Hope that helps. – ranjjose Jul 17 '17 at 09:08
  • Added all relevant info – ranjjose Jul 17 '17 at 10:20
  • I got a chance to execute this on a nexus 9 tablet. And probably since it is a stock android device, it works perfectly as expected. – ranjjose Jul 18 '17 at 06:10

2 Answers2

2

Update: Found a Won't Fix (Infeasible) bug that may be related to the topic. It seems like the Accessibility Service behavior is not consistent across devices. Some will disable the service, some will resume, etc. It fits with user complaints found online, and with your observations well.

The best advise I can give is to make the service as minimal as possible on a different process (so it won't be killed with the app is killed), and also on boot check if it is enabled, and if not - use the notification system to allow quick access to Android Accessibility settings screen, for easy enablement.


I'm not familiar with the specific problem, but I believe this workaround will work for you. I use this code for AlarmManager in production.

In AndroidManifest.xml:

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <receiver android:name=".BootListener">
        <intent-filter>
            <category android:name="android.intent.category.DEFAULT"/>

            <action android:name="android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
    </receiver>

And create a class

public class BootListener extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      // If your bootstrap is in Application class, then it will be called anyway - nothing to do here.
      // else - call your bootsrap here
    }
}

I believe that

(2) If the app is killed, reboots after that won't hit the RECEIVE_BOOT_COMPLETED broadcast receiver

Is wrong, when rebooting, Android has no knowledge that your app was killed. It was never a problem for my app (+1.2M users).

Amir Uval
  • 14,425
  • 4
  • 50
  • 74
  • Thanks auval. I had already tried with `android.intent.category.DEFAULT` filter. Unfortunately it didn't work on three to four test devices (one plus 3T, MI, redmi). The reboots after app kill issue is with regard to `Accessibility service` – ranjjose Jul 23 '17 at 14:09
  • I didn't see a BroadcastReceiver in the code you've posted. – Amir Uval Jul 23 '17 at 14:12
  • Also what I read about `accessibility service` is that when you kill the app, it resets accessibility service permission – ranjjose Jul 23 '17 at 14:12
  • https://stackoverflow.com/a/26349254/1180898 says that killing the Accessibility Service restarts it immediately. Perhaps you mean user disabling the service? – Amir Uval Jul 23 '17 at 14:35
  • Nope. I am talking about killing the app for which you gave accessibility service permission. Also most of these behaviours are device specific. – ranjjose Jul 23 '17 at 14:40
1

PeriodicTask config:

PeriodicTask task = new PeriodicTask.Builder()
                .setService(MyTaskService.class)
                .setTag(TASK_TAG_PERIODIC)
                .setPeriod(30L)
                .setFlex(10L)
                .setExtras(bundle) **/* you put this line here */**
                .setPersisted(true)
                .build();


    PeriodicTask task = new PeriodicTask.Builder()
                    .setService(MyTaskService.class)
                    .setTag(TASK_TAG_PERIODIC)
/* you don't have ".setExtras(bundle)" line here */ 
/* try adding this line from above, as logcat is showing this */
                    .setPeriod(5)
                    .setPersisted(true)
                    .build();
Uddhav P. Gautam
  • 7,362
  • 3
  • 47
  • 64
  • Thanks for noting that. However, that's not the issue. I guess when I copy pasted, I copied the wrong file. The problem is device specific. For instance, in oneplus and MI devices, these issues happen. It is inconsistent too. In fact in oneplus (3T) if you have the word "test" in the package boot_completed is hit!! In MI nothing works. For the time being, I got a work around with accessibility services as mentioned here https://stackoverflow.com/a/41627296/1349159 – ranjjose Jul 20 '17 at 18:30
  • 1
    Yes it is very specific cases of the devices. Many manufacturer makes the flavour of the android and there own security layer which stops the app back ground service to start until and unless user goes and give the exclusive permission. We have replicated this behaviour in the Xiomi mobiles in our development. In that case you have to give some kind of help document to the user. – Devesh Jul 23 '17 at 08:09