Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCMNetworkManager isn't running PeriodicTask after reboot

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.
like image 696
ranjjose Avatar asked Jul 12 '17 20:07

ranjjose


2 Answers

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();
like image 167
Uddhav P. Gautam Avatar answered Nov 12 '22 21:11

Uddhav P. Gautam


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

like image 21
Amir Uval Avatar answered Nov 12 '22 20:11

Amir Uval