Search code examples
javaandroidmanifestreceiver

Android Oreo Receiver Stops If Removed App From Recents


Receiver works on all android versions from 4.2 upto 8.0. Even if app is removed from Recent Apps But if removed from Recent Apps in Android Oreo, it then never triggers receiver again.

my manifest.xml :

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

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

<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.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />

<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:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

    <service
        android:name=".WatchMan"
        android:enabled="true"
        android:exported="true" />

    <receiver
        android:name=".Receiver"
        android:enabled="true"
        android:exported="true">

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

    </receiver>


</application>

My receiver.java :

public class Receiver extends BroadcastReceiver
{
public String PhoneNumber = "UNKNOWN";

@Override
public void onReceive(Context context, Intent intent)
{

    Log.d("RECEIVER :","CAPTURED THE EVENT.....");

    try
    {
        PhoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
        PhoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
             context.startForegroundService(new Intent(context, WatchMan.class));
        }
        else
        {
             context.startService(new Intent(context, WatchMan.class));
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
        Log.e("RECEIVER EXCEPTION : ", "Exception is : ", e);
    }

}

I want to know if i am doing any mistake in code? Android Developers Documentation asking to register receiver runtime using context. Then i searched for registering it in runtime on stackoverflow but looks no proper thread accepted as answer. How can make receiver to to be ready again, even if removed from recents of Android Oreo?

Thanking you in advance.


Solution

  • I have deleted unrelated posts. I am posting final answer as it may help others. @WIZARD help was thankful.


    PHONE_STATE is implicit and will not be triggered on android Oreo or higher. So just place permissions in manifest like :
    

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <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.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    
    <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:excludeFromRecents="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    
        <service
            android:name=".WatchMan"
            android:enabled="true"
            android:exported="true">
        </service>
        <service
            android:name=".CatchNumbers"
            android:enabled="true"
            android:exported="true">
        </service>
    
        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
    
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
    
        </receiver>
    </application>
    

    Register implicit receivers from foreground service :

    public class WatchMan extends Service
    {
    NotificationManager mNotifyManager;
    NotificationCompat.Builder mBuilder;
    NotificationChannel notificationChannel;
    String NOTIFICATION_CHANNEL_ID = "17";
    private boolean running;
    
    private BroadcastReceiver mCallBroadcastReceiver = new BroadcastReceiver()
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            String PhoneNumber = "UNKNOWN";
            Log.d("RECEIVER :  ","HERE HERE");
    
            try
            {
                String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
    
                if(state == null)
                {
                    PhoneNumber = "UNKNOWN";
                }
                else if (state.equals(TelephonyManager.EXTRA_STATE_RINGING))
                {
                    PhoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
                    Log.d("INCOMING ","Incoming number : "+PhoneNumber);
                }
                if(intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL"))
                {
    
                    PhoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
                    Log.d("OUTGOING ","Outgoing number : "+PhoneNumber);
    
                }
    
    
                if(!PhoneNumber.contentEquals("UNKNOWN"))
                {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                    {
                        context.startForegroundService(new Intent(context, CatchNumbers.class));
                    }
                    else
                    {
                        context.startService(new Intent(context, CatchNumbers.class));
                    }
                }
    
    
    
            }
            catch (Exception e)
            {
                e.printStackTrace();
                Log.e("RECEIVER EXCEPTION : ", "Exception is : ", e);
            }
        }
    };
    
    public WatchMan() { }
    
    @Override
    public void onCreate()
    {
        super.onCreate();
    
        mBuilder = new NotificationCompat.Builder(this, null);
    
        IntentFilter filterstate = new IntentFilter();
        filterstate.addAction("android.intent.action.NEW_OUTGOING_CALL");
        filterstate.addAction("android.intent.action.PHONE_STATE");
        this.registerReceiver(mCallBroadcastReceiver, filterstate);
    
        Log.d("RECEIVER : ", "\nCreated....");
    
        mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
        mBuilder = new NotificationCompat.Builder(this, null);
        mBuilder.setContentTitle("Insta Promo")
                .setContentText("Insta Promo Is Up..")
                .setTicker("Insta Promo Is Up..")
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setPriority(Notification.PRIORITY_HIGH)
                .setDefaults(Notification.DEFAULT_ALL)
                .setVisibility(Notification.VISIBILITY_PUBLIC)
                .setOngoing(true)
                .setAutoCancel(false);
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
    
            notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications", NotificationManager.IMPORTANCE_HIGH);
    
            // Configure the notification channel.
            notificationChannel.setDescription("Channel description");
            notificationChannel.enableLights(true);
            notificationChannel.setLightColor(Color.RED);
            notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
            notificationChannel.enableVibration(true);
            notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
            mNotifyManager.createNotificationChannel(notificationChannel);
        }
    
        running = true;
    
        mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
        startForeground(17, mBuilder.build());
    
    
    
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        Log.d("RECEIVER : ", "\nOnStartCommand....");
        new Thread(new Runnable()
        {
            public void run()
            {
                while(running)
                {
                    try
                    {
                        Log.d("RECEIVER : ", "\nALIVE..");
                        Thread.sleep(10000);
                    }
                    catch (InterruptedException e)
                    {
                        Log.d("RECEIVER : ", "\nThread : InterruptedException in Receiver...");
                        Log.e("RECEIVER : ", "\nException is : ", e);
                    }
                    catch (Exception e)
                    {
                        Log.d("RECEIVER : ", "\nThread : Exception Error in Receiver...");
                        Log.e("RECEIVER : ", "\nException is : ", e);
                    }
                }
    
            }
    
        }).start();
    
    
    
        return START_STICKY;
    }
    
    @Override
    public void onDestroy()
    {
        this.unregisterReceiver(mCallBroadcastReceiver);
        running = true;
        Log.d("RECEIVER : ", "\nDestroyed....");
        Log.d("RECEIVER : ", "\nWill be created again....");
    }
    
    @Override
    public IBinder onBind(Intent intent)
    {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
    
    @Override
    public void onTaskRemoved(Intent rootIntent)
    {
        super.onTaskRemoved(rootIntent);
        Log.d("SERVICE : ", "\nTask Removed....");
    }
    
    
    }
    

    There are some intent actions like NEW_OUTGOING_CALL AND BOOT_COMPLETED which are excluded and can be implemented in receiver and placed in manifest like :

    public class MyReceiver extends BroadcastReceiver
    {
    
    @Override
    public void onReceive(Context context, Intent intent)
    {
        Log.d("INSTA_BOOT : ", "\nBOOT_COMPLETE_EVENT_OF_INSTA....");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
            context.startForegroundService(new Intent(context, WatchMan.class));
        }
        else
        {
            context.startService(new Intent(context, WatchMan.class));
        }
    }
    }
    

    As i wanted to re-register or say want to restart foreground service on REBOOT OR BOOT-COMPLETE


    CatchNumbers.java is a simple service which performs operation when receiver triggers perticular actions.
    

    It works good on every restart and as android:excludeFromRecents="true" is not needed anymore as even if user removes it from recents on Oreo it will restart the service as it is STICKY. Hope it helps someone like me..!!