I read this post: Broadcast receiver onReceive() getting called multiple times
But I didn't find the needed answer.
I've created a small utility function that overrides the onReceive() of BroadcastReceiver and rejects an incoming call based on certain conditions (which works fine).
Now once all the conditions are matched and I reject the call, I would like to store that number in the database (which again is an easy task). I would like to save the number in the database once I reject the call. But in the logs I observe that once there is an incoming call, the onReceive function gets called multiple times. If this is the case, I do not want multiple entries in my DB.
Is there any way by which the onReceive() would be called only once ? Or any workaround ?
Thanks for any help
Your receiver will get called for three different state.on ringing, on hook and on idle state.
Check the phone state in onReceive.You may want to cut the call and store in db if its state is ringing.
public void onReceive(final Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (null == bundle) {
return;
}
String state = bundle.getString(TelephonyManager.EXTRA_STATE);
if (TelephonyManager.EXTRA_STATE_RINGING.equalsIgnoreCase(state)) {
//cut the call and store in db
return;
}
}
if(TelephonyManager.EXTRA_STATE_IDLE.equalsIgnoreCase(state)) {
return;
}
if(TelephonyManager.EXTRA_STATE_OFFHOOK.equalsIgnoreCase(state)){
return;
}
}
Related
I have an app that checks the phone number of an incoming call against a blacklist.
I have used the below code for several versions of Android to get the phone number of an incoming call, but when I test it against Android P, it behaves unexpectedly.
For readability, I have removed all null checks from the code below.
public class IncomingCallHandler extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String state = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING)) {
String phoneNumber = bundle
.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
}
}
}
On Android versions less than P, onReceive with state EXTRA_STATE_RINGING may be called several times during an incoming call, but phoneNumber always has the same value (the actual incoming phone number).
On Android P, onReceive is called twice during an incoming call. The first time phoneNumber=null, the second time it is the actual phone number.
Is this a bug? Is it supposed to be like this? Do you get the same in your apps?
I have written an interceptor to access a call data when there is an incoming call. I listen to state change action to decide whether it is a missed call or received call. I have written a code to do things whenever call ends.
public class PhoneStateBroadcastReceiver extends BroadcastReceiver {
public static final String TAG = "PHONE STATE";
private static String mLastState = TelephonyManager.CALL_STATE_IDLE;
#Override
public void onReceive(Context context, Intent intent) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (!state.equals(mLastState)) {
mLastState = state;
Log.e(TAG, state);
return;
}
//Code to do stuff based on last state and state
}
}
The code works fine. But I noticed my app failed to process some of the calls. As this issue was hard to reproduce, I monitored my app for a whole day based on my call activity. When I debugged the issue, I found out that mLastState wasn't changing in such cases. It was still IDLE in case when incoming call ends where it should have been RINGING or OFFHOOK. The reason for that was my app was getting killed and mLastState was being reinitialized. So I was losing the last state.
How to handle this situation? First solution came to my mind was to persist a mLastState to a sharedpreference. Is there any other way to handle this?
I have a BroadcastReceiver which listens for an intent in onDestroy() callback. And there is a blocking while which goes on till bluetooth discoverability is switched off. Once discoverability is off, the changeModeReceiver will call its onReceive() and set destroy_ok to true, and hence breaking out of the while loop. But, this is not giving desired results.
Toast message, "In onDestroy()" is not getting printed
"In onDestroy()" is getting printed in the logcat
The bluetooth is still switched on
The code is as follows.
boolean destroy_ok = false;
protected void onDestroy(){
System.out.println("In onDestroy()");
Toast.makeText(getApplicationContext(), "In onDestroy()", Toast.LENGTH_LONG).show();
BroadcastReceiver changeModeReceiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent){
String mode = intent.getStringExtra(BluetoothAdapter.EXTRA_SCAN_MODE);
if (mode.equals(BluetoothAdapter.SCAN_MODE_NONE))
destroy_ok = true;
}
};
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
registerReceiver (changeModeReceiver, filter);
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,1);
startActivity(discoverableIntent);
while (!destroy_ok){}
unregisterReceiver(changeModeReceiver);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter.isEnabled())
adapter.disable();
System.out.println("Leaving onDestroy()");
super.onDestroy();
}
The onDestroy method (as well as all other activity lifecycle methods, view callback methods, etc.) is called on the application's main UI thread, so no, you shouldn't block for a significant period of time when called. Doing so will likely result in lag, and may even spawn an ANR (application not responding) error if you block for more than 5-10 seconds.
Note: do not count on this method being called as a place for saving
data! For example, if an activity is editing data in a content
provider, those edits should be committed in either onPause() or
onSaveInstanceState(Bundle), not here. This method is usually
implemented to free resources like threads that are associated with an
activity, so that a destroyed activity does not leave such things
around while the rest of its application is still running. There are
situations where the system will simply kill the activity's hosting
process without calling this method (or any others) in it, so it
should not be used to do things that are intended to remain around
after the process goes away.
http://developer.android.com/reference/android/app/Activity.html#onDestroy()
So simply said, use onPause() for such operations. Also, I would use a Service or a new Thread in your case.
I have an alarm application. I generally know the lifecycle of the receiver and how to use WakeLock.
Today however I was contacted by an user that have sent me a really strange log and complained that his alarm hasn't started until he have had unlocked the phone by himself. I used to have problems with phones going back to sleep after receiver completed its work and before activity was started, but creating WakeLock in the receiver seemed to fix the problem. At least until today - from log it seems that onReceive method wasn't called at all until user has unlocked phone by himself.
Facts:
it is the first case I have heard of
it has happened a few times to the user, but not every time
log is prepared by adding text to SQLite database. It doesn't seem to delay application in any significant way
infomation from onReceive was recorded over 100 seconds after expected alarm start time. It is the first method call in onReceive
alarm was started just after user has unlocked the phone
I use AlarmManager.RTC_WAKEUP flag
user says he doesn't have any custom rom. I wait for answer if he has any custom/special lockscreen
phone model is Sony Xperia U ST25A, Android 4.0.4
Any ideas what could be causing this problem? Is it possible that BroadcastReceiver's "inside" WakeLock doesn't work somehow?
EDIT:
I would like to emphasize the issue here - BroadcastReceiver should keep phone awake during its whole onReceive method. However in my case, it is either that
phone falls to sleep before onReceive methods end (even before finishing "logging call")
phone is not awaken by receiver at all
Also, I would like to point out the fact that user has stated clearly - alarm has started precisely when he has unlocked phone by himself. Couple of times.
Some code:
#Override
public void onReceive(Context context, Intent intent) {
Logger.initialize(context, "AlarmReceiver");
...
}
Logger methods:
public synchronized static void initialize(Context context, String text) {
try {
if (mInstance == null) { // this is the block that is runned
BugSenseHandler.initAndStartSession(context, BUGSENSE_ID);
mInstance = new Logger(context);
log("== Logger initialized == from "
+ (text != null ? text : "")); // it stores times as well. Said
// that alarm was started over 100
// seconds after it should
} else {
log("logger initialized again from "
+ (text != null ? text : ""));
}
} catch (Exception e) {
try {
BugSenseHandler.sendException(e);
mInstance = null;
} catch (Exception e2) {
}
}
}
Take a look at WakefulIntentService from Commonsware
public class MyReceiver extends PhoneStateIntentReceiver {
#Override
public void onReceiveIntent(Context context, Intent intent) {
if (intent.action == Intent.CALL_ACTION) {
}
}
}
Assume that notifyPhoneCallState has been called to enable MyReceiver to receive notifications about phone call states, in which case the code will get executed?
when device receives an incoming call
when outgoing call is initiated on the device
when the user presses the call button
incoming phone call is terminated
or will the code not be executed at all?
Did you mean public static final String ACTION_CALL instead of CALL_ACTION?
Activity Action: Perform a call to someone specified by the data.
Input: If nothing, an empty dialer is started; else getData() is URI of a phone number to be dialed or a tel: URI of an explicit phone number.
Output: nothing.
Note: there will be restrictions on which applications can initiate a call; most applications should use the ACTION_DIAL.
Note: this Intent cannot be used to call emergency numbers. Applications can dial emergency numbers using ACTION_DIAL, however.