19

My Broadcast receiver is not working on oreo but its working below oreo it's working fine, I searched a lot regarding this but could not find the suitable solution. Does anyone face the same problem, here is my code regarding my service in which broadcast has been implemented. Kindly suggests me that how I can make in working in oreo.

Here is the class

public int onStartCommand(Intent intent, int flags, int startId) {
        mContext = this;
        mAppPreferences = new AppPreferences(mContext);
        if (intent.getExtras() != null) {
            data = (String) intent.getExtras().get("showPopUp");
            phoneNumber= (String) intent.getExtras().get("dialNumber");
        }
        final IntentFilter intentFilter = new IntentFilter();
        if (data.equalsIgnoreCase("true")) {
            showPopup(getApplicationContext());
            Utils.ApiHit(phoneNumber,getApplicationContext());
        }
        intentFilter.setPriority(2147483647);
        intentFilter.addAction("android.intent.action.PHONE_STATE");
        callExplicitReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
                    if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
                        savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
                    } else {
                        String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
                        phoneNumber = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
                        int state = 0;
                        if (stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
                            state = TelephonyManager.CALL_STATE_IDLE;
                        } else if (stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
                            state = TelephonyManager.CALL_STATE_OFFHOOK;
                        } else if (stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
                            state = TelephonyManager.CALL_STATE_RINGING;
                        }
                        onCallStateChanged(context, state, phoneNumber);
                    }
                }
            }
        };
        mContext.registerReceiver(callExplicitReceiver, intentFilter);
        return START_NOT_STICKY;
    }


    public void onIncomingCallReceived(Context ctx, String number, Date start) {
    }

    public void onIncomingCallAnswered(Context ctx, String number, Date start) {
        if (popupView.getVisibility() == View.GONE) {
            popupView.setVisibility(View.VISIBLE);
        }
    }

    public void onIncomingCallEnded(Context ctx, String number, Date start, Date end) {

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                DeleteCallLogByNumber(number);
            }
        }, 2000);
        if (popupView.getVisibility() == View.VISIBLE) {
            popupView.setVisibility(View.GONE);
        }
    }

    public void onOutgoingCallStarted(Context ctx, String number, Date start) {
//        mAppPreferences.setPrefrenceString("busy", "yes");
//        if (data.equalsIgnoreCase("true")) {
            mediaPlayer = MediaPlayer.create(ctx, R.raw.speech_audio);
//        } else {
//            mediaPlayer = MediaPlayer.create(ctx, R.raw.speech_audio);
//        }

        mediaPlayer.start();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                    mediaPlayer.stop();
                    mediaPlayer.release();
                }
            }
        }, 12000);
        if (popupView.getVisibility() == View.GONE) {
            popupView.setVisibility(View.VISIBLE);
        }
    }


    public void onOutgoingCallEnded(Context ctx, String number, Date start, Date end) {
        mAppPreferences.setPrefrenceString("busy", "no");
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                DeleteCallLogByNumber(phoneNumber);
            }
        }, 2000);
        if (popupView.getVisibility() == View.VISIBLE) {
            popupView.setVisibility(View.GONE);
        }
    }

    public void onMissedCall(Context ctx, String number, Date start) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                DeleteCallLogByNumber(phoneNumber);
            }
        }, 2000);
        if (popupView.getVisibility() == View.VISIBLE) {
            popupView.setVisibility(View.GONE);
        }
    }




public void onCallStateChanged(Context context, int state, String number) {
        if (lastState == state) {
            return;
        }
        switch (state) {
            case TelephonyManager.CALL_STATE_RINGING:
                isIncoming = true;
                callStartTime = new Date();
                savedNumber = number;
                onIncomingCallReceived(context, number, callStartTime);
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                if (lastState != TelephonyManager.CALL_STATE_RINGING) {
                    isIncoming = false;
                    callStartTime = new Date();
                    onOutgoingCallStarted(context, savedNumber, callStartTime);
                } else {
                    isIncoming = true;
                    callStartTime = new Date();
                    onIncomingCallAnswered(context, savedNumber, callStartTime);
                }
                break;
            case TelephonyManager.CALL_STATE_IDLE:
                if (popupView.getVisibility() == View.VISIBLE) {
                    popupView.setVisibility(View.GONE);
                }
                if (lastState == TelephonyManager.CALL_STATE_RINGING) {
                    onMissedCall(context, savedNumber, callStartTime);
                } else if (isIncoming) {
                    onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
                } else {
                    onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
                }
                break;
        }
        lastState = state;
    }

    @Override
    public void onDestroy() {
        mContext.unregisterReceiver(callExplicitReceiver);
    }

Noting is in coming inside receiever,Can anyone help me out in this?

New Additions as per discussion

Manifest data :-

Permission used :-

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

Reciver:-

<receiver android:name="com.example.dialer.AppUtils.StartUpBootReceiver" android:enabled="true" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>

My BroadCast Reciever Class :-

public class StartUpBootReceiver extends BroadcastReceiver {

    private Context mContext;

    @Override
    public void onReceive(Context context, Intent intent) {
        mContext= context;
        String action = "START";

        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
            {
                context.startForegroundService(new Intent(context, PhoneStateService.class));
            }
            else
            {
                context.startService(new Intent(context, PhoneStateService.class));
            }
        }
    }


    private boolean isServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
}

Rest the same service will get the call, but the problem is i still does not get call in receiver.And m primary point is that service should only get called once user tap on button , not automatically as i have to pass some values in the service.

Thanks

Yogesh Rathi
  • 6,331
  • 4
  • 51
  • 81
Era Singla
  • 266
  • 1
  • 2
  • 8
  • I got your request; There is no good documentation on this by google and no sample implementations examples too.. But i have implemented it in oreo and even tested on android P the upcoming android; working fine... – sandhya sasane Jul 07 '18 at 12:12
  • I am going to write the answer and you are suggested to follow it and let me know the feedback over it.. – sandhya sasane Jul 07 '18 at 12:13
  • START_NOT_STICKY removing your service as soon as it is executed just once... And as there is no service running in the background next time... your call broadcast receiver is also not there to receive the broadcasts. It should be START_STICKY to remain always there in background – sandhya sasane Jul 07 '18 at 12:22
  • Again as your service which is implementing receiver will be always running ( ongoing process ) should be notified by a notification as soon as service oncreate is called so that onstartcommand will remain active all the time – sandhya sasane Jul 07 '18 at 12:24
  • I will suggest do not implement any code as of now : break it in tasks.. 1) Implement only on boot complete receiver by registering in manifest.xml 2) From receiver call your service which will just print hello 3) Upload above code with manifest.xml ; receiver and service code here and let me see it 4) Then implement runtime broadcast receiver in service for receiving call broadcasts... – sandhya sasane Jul 07 '18 at 12:28
  • Hii @sandhyasasane so you mean I just need to change START_NOT_STICKY to START_STICKY – Era Singla Jul 07 '18 at 12:29
  • Not necessary that the problem is START_NOT_STICKY only... But it must be START_STICKY to remain runtime broadcast receiver to be there for you always. – sandhya sasane Jul 07 '18 at 12:31
  • There are so many things co-related... 1) If service is STICKY then it must notify the ONGOING notification 2) Manifest receivers are now restrictions so.. only implement BOOT COMPLETE receiver and register rest receivers from a service – sandhya sasane Jul 07 '18 at 12:36
  • Forget all and Implement a simple hello world app which implements BOOT_Complete . Then from that receiver call a intentservice. Then from Intentservice implement runtime call receiver. Then as service will be running always make it START_STICKY... As service is always running... Notify a notification with ONGOING tag... Every line is a step.. by ... step... – sandhya sasane Jul 07 '18 at 12:40
  • @sandhyasasane , Please have a look on updated question – Era Singla Jul 07 '18 at 13:21
  • Part 1) Comment the calls to the service PhoneStateService in onReceive of the StartUpBootReceiver class and just Toast a simple text "Boot Complete" and let me know does it toasts that message when device restarts...? – sandhya sasane Jul 07 '18 at 16:00
  • If Part 1) is a Success.... Then... Part 2) Create a intentservice .... Not by code.... Instead... By Going to project structure -> Right click -> New-> Service -> Service ( IntentService ).... Name it... It will create a intentservice for you and will update manifest.xmml on its own – sandhya sasane Jul 07 '18 at 16:09
  • If Part 2 is done Then ... By right clicking on service class name in code editor... Generate codes for ... onCreate, onStartCommand, onDestroy, onBind. Remember do not code ... let it create code automatically for you as directed here... Then make onStartCommand START_STICKY and print some text using log... and then uncomment the onReceive calls to the service of StartUpBootReceiver class and cross check... does it logs or not when the device is restarted.... – sandhya sasane Jul 07 '18 at 16:17
  • Era singla, Is your problem solved...? – sandhya sasane Jul 09 '18 at 05:37
  • Hi @sandhyasasane , No it's not working, even my Boot receiver is not working, when I restart my device. – Era Singla Jul 09 '18 at 06:26
  • @sandhyasasane , let me know one thing that is Boot receiver important? Can it be done without Boot receiver? I even tried this by making a separate class for my phone state Broadcast receiver and declared that in my manifest, but still no progress. – Era Singla Jul 09 '18 at 07:30
  • @Era Singla Yes.., Boot receiver is important... provide me email... I have working code... I will mail you the project ... which receives call broadcasts and notifies user... Just Import that project... and modify as per your need... OKAY – sandhya sasane Jul 09 '18 at 16:45
  • https://stackoverflow.com/a/50628103/8572503 @EraSingla I think it will help you. It has the perfect answer you want. – exploitr Jul 11 '18 at 05:11
  • @sandhyasasane my broadcast receiver is not calling in some phones – k_kumar Jun 18 '19 at 05:45
  • you can try this https://github.com/devggaurav/BroadcastReceiver-For-Naught-and-Oreo-devices @EraSingla – Danial clarc Sep 25 '19 at 13:38
  • This worked for me: (https://stackoverflow.com/questions/52364398/broadcast-receiver-for-nought-and-oreo-devices-not-working) – quilkin Dec 06 '20 at 16:19

6 Answers6

19

Broadcast Limitations

If an app registers to receive broadcasts, the app's receiver consumes resources every time the broadcast is sent. This can cause problems if too many apps register to receive broadcasts based on system events; a system event that triggers a broadcast can cause all of those apps to consume resources in rapid succession, impairing the user experience. To mitigate this problem, Android 7.0 (API level 25) placed limitations on broadcasts, as described in Background Optimization. Android 8.0 (API level 26) makes these limitations more stringent.

  1. Apps that target Android 8.0 or higher can no longer register broadcast receivers for implicit broadcasts in their manifest. An implicit broadcast is a broadcast that does not target that app specifically. For example, ACTION_PACKAGE_REPLACED is an implicit broadcast, since it is sent to all registered listeners, letting them know that some package on the device was replaced. However, ACTION_MY_PACKAGE_REPLACED is not an implicit broadcast, since it is sent only to the app whose package was replaced, no matter how many other apps have registered listeners for that broadcast.

  2. Apps can continue to register for explicit broadcasts in their manifests.

  3. Apps can use Context.registerReceiver() at runtime to register a receiver for any broadcast, whether implicit or explicit.

  4. Broadcasts that require a signature permission are exempted from this restriction, since these broadcasts are only sent to apps that are signed with the same certificate, not to all the apps on the device.

From the Official Documentation

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Mayank Bhatnagar
  • 2,120
  • 1
  • 12
  • 20
  • @Makank Do I need to add package name in action? – Era Singla Jul 07 '18 at 11:44
  • 3
    Check this out https://commonsware.com/blog/2017/04/11/android-o-implicit-broadcast-ban.html – Mayank Bhatnagar Jul 07 '18 at 14:37
  • 1
    @MayankBhatnagar some intent actions were excluded from that ban – exploitr Jul 09 '18 at 14:57
  • 1
    @MayankBhatnagar if the value of _action_ of a static broadcast receiver is arbitrary then would it be an implicit or explicit receiver? – Neeraj Sewani Oct 03 '18 at 07:18
  • 1
    What will happen if we use a broadcast which is not permitted by android Oreo? Will it not work or will the app crash? – Rajesh K Dec 16 '18 at 08:34
  • @RajeshK it will not crash. Just BroadcastReceiver will not receive Intent. – hidd Apr 18 '19 at 15:35
  • Actually this answer is not relevant. OP has one manifest -registered broadcast, for `ACTION_BOOT_COMPLETED`. That broadcast is in the list of exceptions to the rule. Please read the rest of the documentation that you linked to: https://developer.android.com/guide/components/broadcast-exceptions.html – David Wasser Jul 30 '19 at 13:39
  • My service started perfectly on boot before Oreo, but not after. The only solution I found is here (https://stackoverflow.com/questions/52364398/broadcast-receiver-for-nought-and-oreo-devices-not-working) – quilkin Dec 06 '20 at 16:22
5

The problem comes with the service you're trying to run, services or persistent background services are not permitted to run for long for apps targeting Oreo and above.

Check this guide and this as well for migrating your app to support Oreo.

MadScientist
  • 2,134
  • 14
  • 27
  • 1
    Can you provide any reference rather than google developer site they simply provide a hint and no real time examples. Because since November 2018 I facing this background crash issue in SAMSUNG and NEXUS devices. Why Google did not think from a developer perspective and making us in trouble like this, it is my biggest worry nowadays. – MohanRaj S Dec 28 '18 at 05:34
5

I also had this kind of issue, but I found a better solution:

Class MyReceiver

@BroadcastReceiverActions({
        "android.intent.action.SCREEN_ON",
        "android.intent.action.SCREEN_OFF",
        "android.intent.action.DREAMING_STARTED",
        "android.intent.action.DREAMING_STOPPED",
        "android.intent.action.ACTION_POWER_DISCONNECTED",
        "android.intent.action.ACTION_POWER_CONNECTED",
        "android.net.conn.CONNECTIVITY_CHANGE"
})
public class MyReceiver extends BroadcastReceiver {

    public MyReceiver() {
        super();
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Session.getGlobalReceiverCallBack(context, intent);

        //Log.e("dfd", "" + intent.getAction());
    }
}

Class AppController

public class AppController extends Application {

    private BroadcastReceiver receiver;
    MyReceiver mR;

    @Override
    public void onCreate() {
        super.onCreate();
        mR = new MyReceiver();
        receiver = DynamicReceiver.with(mR)
                .register(this);

    }
}

Class MainActivity

public class MainActivity extends AppCompatActivity implements GlobalReceiverCallBack {

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

    }

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

        Toast.makeText(context, "" + intent.getAction(), Toast.LENGTH_LONG).show();
    }
}

For complete reference you can see also https://github.com/devggaurav/BroadcastReceiver-For-Naught-and-Oreo-devices

Sagar
  • 3,107
  • 2
  • 26
  • 35
Akshay Kumar
  • 71
  • 2
  • 2
1

Android 8.0 offers several improvements to JobScheduler that make it easier to replace services and broadcast receivers with scheduled jobs:

https://developer.android.com/about/versions/oreo/background

In many cases, apps that previously registered for an implicit broadcast can get similar functionality by using a JobScheduler job. For example, a social photo app might need to perform cleanup on its data from time to time, and prefer to do this when the device is connected to a charger. Previously, the app registered a receiver for ACTION_POWER_CONNECTED in its manifest; when the app received that broadcast, it would check whether cleanup was necessary. To migrate to Android 8.0 or higher, the app removes that receiver from its manifest. Instead, the app schedules a cleanup job that runs when the device is idle and charging.

Vlad
  • 7,997
  • 3
  • 56
  • 43
1

Register your broadcast receiver in activity on create method rather than in manifest and unregister it on destroy method. Hope this will work on android 9.

0

I have faced the similar issue when implementing call recording app,

I have added the following code in the AndroidManifest.xml file, then the register is working normally

         <receiver android:name=".Services.Receiver"
             android:permission="android.permission.RECEIVE_BOOT_COMPLETED"
             android:enabled="true"
             android:exported="true">
            <intent-filter>

                <action android:name="android.intent.action.PHONE_STATE"/>
                <action android:name="android.intent.action.ANSWER"/>
                <action android:name="android.intent.action.CALL_BUTTON"/>
                <action android:name= "android.intent.action.NEW_OUTGOING_CALL"/>
                <action android:name="android.intent.action.BOOT_COMPLETED" />

            </intent-filter>
         </receiver>

sai Pavan Kumar
  • 1,129
  • 12
  • 20