1

In the code below, I have a boot_completed BroadcastReceiver, where in onReceive I register another BroadcastReceiver to monitor calls changes. But it throws ReceiverCallNotAllowedException, as according to document this happens when trying to register a BroadcastReceiver from within another BroadcastReceiver. So, what's your suggestion in this case, knowing that I don't want to launch an activity to register the call receiver.

ReceiverCallNotAllowedException This exception is thrown from Context#registerReceiver and Context#bindService when these methods are being used from an BroadcastReceiver component. In this case, the component will no longer be active upon returning from receiving the Intent, so it is not valid to use asynchronous APIs.

ServiceStarter.java (The one receives boot_completed intent)

public class ServiceStarter extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
    if(intent != null && intent.getAction() != null){
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction()) || Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction())) {
           
            registerCallReceiver(context);
        }
    }
}

public void registerCallReceiver(Context aCtx){
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("android.intent.action.PHONE_STATE");
    
    // CallReceiver extends BroadcastReceiver
    CallReceiver receiver = new CallReceiver();
    aCtx.registerReceiver(receiver, intentFilter);
}

AndroidManifest.xml

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

    <!--This part is inside the application-->
    <receiver android:name=".CallReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>
    </receiver>

I'm considering having a dummy service, that I start it from the ServiceStarter class, and use this dummy service to register the CallReceiver. What do you think.

Edit 1:

I created a dummy service to be started by ServiceStarter (A receiver gets called when the mobile boots), and in the dummy service I register the CallReceiver (BroadcastReceiver that monitors changes in calls status). But I ended up with the following exception:

Service has leaked IntentReceiver in android

ServiceStarter.java (The one receives boot_completed intent)

public class ServiceStarter extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
    if(intent != null && intent.getAction() != null){
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction()) || Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction())) {
           
            startDummyService(context);
        }
    }
}

public void startDummyService(Context aCtx){
    Intent intent = new Intent(aCtx, DummyService.class);
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
        aCtx.startForegroundService(intent);
    } else {
        aCtx.startService(intent);
    }
}

DummyService.java (Where I register CallReceiver broadcast)

public class DummyService extends Service {

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

    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("android.intent.action.PHONE_STATE");
    CallReceiver receiver = new CallReceiver();
    registerReceiver(receiver, intentFilter);
}

@Override
public int onStartCommand(Intent aIntent, int aFlags, int aStartId){
    super.onStartCommand(aIntent, aFlags, aStartId);

    return START_STICKY;
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}
}
Ashraf Alshahawy
  • 1,139
  • 1
  • 14
  • 38

2 Answers2

4

I finally found the solution to the ReceiverCallNotAllowedException that takes place when registering a BroadcastReceiver (CallReceiver) from another BroadcastReceiver (ServiceStarter).

This will cause the exception:

public void registerCallReceiver(Context aCtx){
   aCtx.registerReceiver(receiver, intentFilter);
}

This will not cause the exception:

public void registerCallReceiver(Context aCtx){
   aCtx.getApplicationContext().registerReceiver(receiver, intentFilter);
}
Andreas Løve Selvik
  • 1,262
  • 16
  • 25
Ashraf Alshahawy
  • 1,139
  • 1
  • 14
  • 38
0

So, what's your suggestion in this case, knowing that I don't want to launch an activity to register the call receiver

Register for the broadcast in the manifest. That particular broadcast appears in the list of implicit broadcasts for which you can register in the manifest.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • The 2nd broadcast is for monitoring calls changes, which requires READ_PHONE_STATE permission, and it can't be implicit on latest Android version. I added more code above. – Ashraf Alshahawy Jul 03 '21 at 20:41
  • @AshrafAlshahawy: Then you will need to use a foreground service and have it call `registerReceiver()`. I think that your boot-time receiver can start that foreground service, though I forget if there are recent limitations on that. – CommonsWare Jul 03 '21 at 20:47
  • 1
    I created a dummy service as a mediator between the ServiceStarter (Receiver) and the CallReceiver. And I ended up with "Service has leaked IntentReceiver in android". It expects me to unregister CallReceiver in onDestory of the dummy service. Which defeats the purpose, I want CallReceiver to be alive all the time to get notified when there's a change in the call status. – Ashraf Alshahawy Jul 04 '21 at 02:28
  • @AshrafAlshahawy: "I want CallReceiver to be alive all the time to get notified when there's a change in the call status" -- your foreground service needs to be running all the time. – CommonsWare Jul 04 '21 at 10:59
  • @CommonWare I'm aware of that. But I can't accomplish this when mobile boots. I can't register CallReceiver from ServiceStarter (boot_completed) because both are BroadcastReceiver. And when I used a mediator (DummyService) between both of them, to register CallReceiver, I ended up with "Service has leaked IntentReceiver". I'm sure there's a solution out there for this situation. Thanks in advance. – Ashraf Alshahawy Jul 04 '21 at 23:33
  • @AshrafAlshahawy: "I'm aware of that" -- you did not implement a foreground service. You called `startForegroundService()`, which is a good starting point. However, the service then has a few seconds in which to call `startForeground()`, supplying a valid `Notification`. The code in your question does not show that. As a result, Android is treating your service as a regular service; there will be messages in Logcat to that effect. And, after a minute, Android will stop the service, at which point you should get that leak message. – CommonsWare Jul 04 '21 at 23:59
  • 1
    @AshrafAlshahawy: You can read more about foreground services, such as the permission that you need to request in the manifest, in [the documentation](https://developer.android.com/guide/components/foreground-services). – CommonsWare Jul 05 '21 at 00:02
  • I finally by a miracle and a continuous search found a solution. Where I don't need to use a DummyService as a mediator between both BroadcastReceivers and to avoid the exception. It's pretty simple aCtx.getApplicationContext().registerReceiver(receiver, intentFilter); The secret is using getApplicationContext(). Thank you so much for your help, really appreciated :) – Ashraf Alshahawy Jul 05 '21 at 00:07
  • could you help with this question? Thank you in advance https://stackoverflow.com/questions/68343989/isgesturedetectionavailable-returns-false-on-android-10 – Ashraf Alshahawy Jul 16 '21 at 14:03