Search code examples
javaandroidbroadcastreceiver

Receiver not getting invoked if removed app from recents


I am developing app which sends SMS on active call ends.

Manifest.xml

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

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<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"
        android:label="@string/app_name"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service
        android:name=".MonitorService"
        android:enabled="true"
        android:exported="true" />
    <service
        android:name=".SendText"
        android:exported="false" />

    <receiver
        android:name=".BootReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
</application>

</manifest>

I have taken all the runtime permissions if its above Marshmallow too from MainActivity.

My Receiver :

public class BootReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
    Log.d("BootTest : ", "\nOnBootReceiver - Received a broadcast!");
    Toast.makeText(context, "InstaPromo Is Ready !!", Toast.LENGTH_LONG).show();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    {
        context.startForegroundService(new Intent(context, MonitorService.class));
    }
    else
    {
        context.startService(new Intent(context, MonitorService.class));
    }
}
}

Implemented receiver runtime as :

public class MonitorService extends Service
{
// Notification variables

private BroadcastReceiver mCallBroadcastReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        String action = intent.getAction();

        if (action.equalsIgnoreCase("android.intent.action.PHONE_STATE"))
        {
            if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING))
            {
                Log.d("RECEIVER X: ", "INCOMING CALL...");
            }
            if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_IDLE))
            {
                Log.d("RECEIVER X: ", "CALL ENDS HERE...");
                Intent Dispatcher = new Intent(context, SendText.class);
                startService(Dispatcher);
            }
            if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
            {
                Log.d("RECEIVER X: ", "ACTIVE CALL GOING ON...");
            }
        }
    }
};

public MonitorService() { }

@Override
public void onCreate()
{
    super.onCreate();

    // created notification here
    // also registered broadcast receiver here

    mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
    startForeground(17, mBuilder.build());
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
    Log.d("WatchMan : ", "\nmCallBroadcastReceiver Listening....");

    //return super.onStartCommand(intent, flags, startId);

    return START_STICKY;
}

@Override
public void onDestroy()
{
    this.unregisterReceiver(mCallBroadcastReceiver);
    Log.d("WatchMan : ", "\nDestroyed....");
    Log.d("WatchMan : ", "\nWill be created again....");
}

@Override
public IBinder onBind(Intent intent)
{
    // TODO: Return the communication channel to the service.
    throw new UnsupportedOperationException("Not yet implemented");
}
}
  • Broadcastreceiver works fine from service class; even if App removed from recent app list, But If removed from recents then on next boot_complete it never calls receiver again. If App is not removed from recents... then on every boot_complete / quick boot it gets triggered and broadcast-receiver works perfectly. I have tried android:exclude_from_recents..., But it is not the way to achieve it.

Can someone please help me to resolve this situation. Thanks

Update 2

I have studied Firebase JobDispatcher documentation over github and used it as belows :

  1. Implemented dependancy in gradle project file
  2. Success in sync thenafter
  3. Created a JobService
  4. Created a job and scheduled it.
  5. It is getting triggered on boot_completed too..

public class MyJobService extends JobService
{
@Override
public boolean onStartJob(JobParameters job)
{
    // Do some work here

    Log.d("MY_JOB :", "STARTED HERE...  \n");

    return false;

    // Answers the question: "Is there still work going on?"
}

@Override
public boolean onStopJob(JobParameters job)
{
    Log.d("MY_JOB :", "STOPPED HERE...  \n");

    return true;

    // Answers the question: "Should this job be retried?"
}
}

        FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(MainActivity.this));

    Bundle myExtrasBundle = new Bundle();
    myExtrasBundle.putString("some_key", "some_value");

    Job myJob = dispatcher.newJobBuilder()
            // the JobService that will be called
            .setService(MyJobService.class)
            // uniquely identifies the job
            .setTag("my-unique-tag")
            // one-off job
            .setRecurring(false)
            // don't persist past a device reboot
            .setLifetime(Lifetime.FOREVER)
            // start between 0 and 60 seconds from now
            .setTrigger(Trigger.executionWindow(10, 10+2))
            // don't overwrite an existing job with the same tag
            .setReplaceCurrent(false)
            // retry with exponential backoff
            .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
            // constraints that need to be satisfied for the job to run
            .setConstraints(
                    // only run when device is idle
                    Constraint.DEVICE_IDLE
            )
            .setExtras(myExtrasBundle)
            .build();

    dispatcher.mustSchedule(myJob);

How should i schedule it just for running once on reboot/boot_complete

  1. I want to recur only once stopped.. : setRecurring(false), IS IT OKAY to false or true it should be true?
  2. setLifetime(Lifetime.UNTIL_NEXT_BOOT) SET it to FOREVER THIS IS OKAY
  3. setTrigger(Trigger.executionWindow(0, 60)) NOT GETTING THIS
  4. setReplaceCurrent(false) YES ITS OKAY.
  5. retry with exponential backoff What is This?

How to set these tags to get started on BOOT / REBOOT and only once, try if fails to start, do not start again ..?


Solution

  • Use FirebaseJobDispatcher to restart your service after every boot. For more on JobDispatcher please see this.