Improve hidden number detection
This commit is contained in:
parent
24f4dd5881
commit
fa5b616573
|
@ -27,7 +27,8 @@ public class CallMonitoringService extends Service {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CallMonitoringService.class);
|
private static final Logger LOG = LoggerFactory.getLogger(CallMonitoringService.class);
|
||||||
|
|
||||||
private final MyPhoneStateListener phoneStateListener = new MyPhoneStateListener();
|
private final MyPhoneStateListener phoneStateListener = new MyPhoneStateListener();
|
||||||
private final CallReceiver callReceiver = new CallReceiver(true);
|
private final CallReceiver callReceiver = new CallReceiver(
|
||||||
|
PhoneStateHandler.Source.PHONE_STATE_BROADCAST_RECEIVER_MONITORING);
|
||||||
|
|
||||||
private boolean monitoringStarted;
|
private boolean monitoringStarted;
|
||||||
|
|
||||||
|
@ -135,15 +136,17 @@ public class CallMonitoringService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
PhoneStateHandler phoneStateHandler = YacbHolder.getPhoneStateHandler();
|
PhoneStateHandler phoneStateHandler = YacbHolder.getPhoneStateHandler();
|
||||||
|
PhoneStateHandler.Source source = PhoneStateHandler.Source.PHONE_STATE_LISTENER;
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case TelephonyManager.CALL_STATE_IDLE:
|
case TelephonyManager.CALL_STATE_IDLE:
|
||||||
phoneStateHandler.onIdle(phoneNumber);
|
phoneStateHandler.onIdle(source, phoneNumber);
|
||||||
break;
|
break;
|
||||||
case TelephonyManager.CALL_STATE_RINGING:
|
case TelephonyManager.CALL_STATE_RINGING:
|
||||||
phoneStateHandler.onRinging(phoneNumber);
|
phoneStateHandler.onRinging(source, phoneNumber);
|
||||||
break;
|
break;
|
||||||
case TelephonyManager.CALL_STATE_OFFHOOK:
|
case TelephonyManager.CALL_STATE_OFFHOOK:
|
||||||
phoneStateHandler.onOffHook(phoneNumber);
|
phoneStateHandler.onOffHook(source, phoneNumber);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,20 +15,20 @@ public class CallReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CallReceiver.class);
|
private static final Logger LOG = LoggerFactory.getLogger(CallReceiver.class);
|
||||||
|
|
||||||
private final boolean monitoringService;
|
private final PhoneStateHandler.Source source;
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "RedundantSuppression"}) // required for BroadcastReceivers
|
@SuppressWarnings({"unused", "RedundantSuppression"}) // required for BroadcastReceivers
|
||||||
public CallReceiver() {
|
public CallReceiver() {
|
||||||
this(false);
|
this(PhoneStateHandler.Source.PHONE_STATE_BROADCAST_RECEIVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CallReceiver(boolean monitoringService) {
|
public CallReceiver(PhoneStateHandler.Source source) {
|
||||||
this.monitoringService = monitoringService;
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
LOG.debug("onReceive() invoked, monitoringService={}", monitoringService);
|
LOG.debug("onReceive() invoked, source={}", source);
|
||||||
|
|
||||||
if (!TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(intent.getAction())
|
if (!TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(intent.getAction())
|
||||||
&& !TelephonyManager.EXTRA_STATE_RINGING.equals(intent.getAction())) { // TODO: check
|
&& !TelephonyManager.EXTRA_STATE_RINGING.equals(intent.getAction())) { // TODO: check
|
||||||
|
@ -58,11 +58,11 @@ public class CallReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
PhoneStateHandler phoneStateHandler = YacbHolder.getPhoneStateHandler();
|
PhoneStateHandler phoneStateHandler = YacbHolder.getPhoneStateHandler();
|
||||||
if (TelephonyManager.EXTRA_STATE_RINGING.equals(telephonyExtraState)) {
|
if (TelephonyManager.EXTRA_STATE_RINGING.equals(telephonyExtraState)) {
|
||||||
phoneStateHandler.onRinging(incomingNumber);
|
phoneStateHandler.onRinging(source, incomingNumber);
|
||||||
} else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(telephonyExtraState)) {
|
} else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(telephonyExtraState)) {
|
||||||
phoneStateHandler.onOffHook(incomingNumber);
|
phoneStateHandler.onOffHook(source, incomingNumber);
|
||||||
} else if (TelephonyManager.EXTRA_STATE_IDLE.equals(telephonyExtraState)) {
|
} else if (TelephonyManager.EXTRA_STATE_IDLE.equals(telephonyExtraState)) {
|
||||||
phoneStateHandler.onIdle(incomingNumber);
|
phoneStateHandler.onIdle(source, incomingNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ package dummydomain.yetanothercallblocker;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.core.util.Predicate;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -21,6 +23,12 @@ import static dummydomain.yetanothercallblocker.EventUtils.postEvent;
|
||||||
|
|
||||||
public class PhoneStateHandler {
|
public class PhoneStateHandler {
|
||||||
|
|
||||||
|
public enum Source {
|
||||||
|
PHONE_STATE_LISTENER,
|
||||||
|
PHONE_STATE_BROADCAST_RECEIVER_MONITORING,
|
||||||
|
PHONE_STATE_BROADCAST_RECEIVER
|
||||||
|
}
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(PhoneStateHandler.class);
|
private static final Logger LOG = LoggerFactory.getLogger(PhoneStateHandler.class);
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -42,8 +50,8 @@ public class PhoneStateHandler {
|
||||||
this.notificationService = notificationService;
|
this.notificationService = notificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRinging(String phoneNumber) {
|
public void onRinging(Source source, String phoneNumber) {
|
||||||
LOG.debug("onRinging({})", phoneNumber);
|
LOG.debug("onRinging({}, \"{}\")", source, phoneNumber);
|
||||||
|
|
||||||
boolean ignore = false;
|
boolean ignore = false;
|
||||||
|
|
||||||
|
@ -53,17 +61,30 @@ public class PhoneStateHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check
|
if (source == Source.PHONE_STATE_LISTENER) {
|
||||||
|
LOG.info("onRinging() treating null from PhoneStateListener as a hidden number");
|
||||||
|
phoneNumber = "";
|
||||||
|
} else if ((source == Source.PHONE_STATE_BROADCAST_RECEIVER_MONITORING
|
||||||
|
|| source == Source.PHONE_STATE_BROADCAST_RECEIVER)
|
||||||
|
&& isEventPresent(sameSourceAndNumber(source, null))
|
||||||
|
&& !isEventPresent(nonEmptyNumber())) {
|
||||||
|
LOG.info("onRinging() treating repeated null from PhoneStateBroadcastReceiver" +
|
||||||
|
" as a hidden number");
|
||||||
|
phoneNumber = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phoneNumber == null) {
|
||||||
LOG.debug("onRinging() ignoring null");
|
LOG.debug("onRinging() ignoring null");
|
||||||
ignore = true;
|
ignore = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ignore && !shouldProcess(phoneNumber)) {
|
if (!ignore && isEventPresent(sameNumber(phoneNumber))) {
|
||||||
LOG.debug("onRinging() ignoring repeated event");
|
LOG.debug("onRinging() ignoring repeated event");
|
||||||
ignore = true;
|
ignore = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
recordEvent(phoneNumber);
|
recordEvent(source, phoneNumber);
|
||||||
|
|
||||||
if (ignore) return;
|
if (ignore) return;
|
||||||
|
|
||||||
|
@ -95,16 +116,16 @@ public class PhoneStateHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onOffHook(String phoneNumber) {
|
public void onOffHook(Source source, String phoneNumber) {
|
||||||
LOG.debug("onOffHook({})", phoneNumber);
|
LOG.debug("onOffHook({}, \"{}\")", source, phoneNumber);
|
||||||
|
|
||||||
isOffHook = true;
|
isOffHook = true;
|
||||||
|
|
||||||
postEvent(new CallOngoingEvent());
|
postEvent(new CallOngoingEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onIdle(String phoneNumber) {
|
public void onIdle(Source source, String phoneNumber) {
|
||||||
LOG.debug("onIdle({})", phoneNumber);
|
LOG.debug("onIdle({}, \"{}\")", source, phoneNumber);
|
||||||
|
|
||||||
isOffHook = false;
|
isOffHook = false;
|
||||||
|
|
||||||
|
@ -113,38 +134,56 @@ public class PhoneStateHandler {
|
||||||
postEvent(new CallEndedEvent());
|
postEvent(new CallEndedEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldProcess(String phoneNumber) {
|
private static Predicate<CallEvent> sameNumber(String number) {
|
||||||
|
return event -> TextUtils.equals(event.number, number);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Predicate<CallEvent> sameSourceAndNumber(Source source, String number) {
|
||||||
|
return event -> event.source == source && TextUtils.equals(event.number, number);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Predicate<CallEvent> nonEmptyNumber() {
|
||||||
|
return event -> !TextUtils.isEmpty(event.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEventPresent(Predicate<CallEvent> predicate) {
|
||||||
|
return findEvent(predicate) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CallEvent findEvent(Predicate<CallEvent> predicate) {
|
||||||
// using 1 second ago as the cutoff point - consider everything older as unrelated events
|
// using 1 second ago as the cutoff point - consider everything older as unrelated events
|
||||||
long cutoff = System.nanoTime() - TimeUnit.SECONDS.toNanos(1);
|
long cutoff = System.nanoTime() - TimeUnit.SECONDS.toNanos(1);
|
||||||
|
|
||||||
if (lastEventTimestamp - cutoff < 0) { // no events in the last second
|
if (lastEventTimestamp - cutoff < 0) { // no events in the last second
|
||||||
lastEvents.clear();
|
lastEvents.clear();
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ListIterator<CallEvent> it = lastEvents.listIterator(); it.hasNext(); ) {
|
for (ListIterator<CallEvent> it = lastEvents.listIterator(); it.hasNext(); ) {
|
||||||
CallEvent event = it.next();
|
CallEvent event = it.next();
|
||||||
if (event.timestamp - cutoff < 0) { // event is older than the cutoff point
|
if (event.timestamp - cutoff < 0) { // event is older than the cutoff point
|
||||||
it.remove();
|
it.remove();
|
||||||
} else if (TextUtils.equals(event.number, phoneNumber)) {
|
} else if (predicate.test(event)) {
|
||||||
return false; // don't process same event
|
return event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recordEvent(String phoneNumber) {
|
private void recordEvent(Source source, String phoneNumber) {
|
||||||
long currentTimestamp = System.nanoTime();
|
long currentTimestamp = System.nanoTime();
|
||||||
lastEvents.add(new CallEvent(phoneNumber, currentTimestamp));
|
lastEvents.add(new CallEvent(source, phoneNumber, currentTimestamp));
|
||||||
lastEventTimestamp = currentTimestamp;
|
lastEventTimestamp = currentTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class CallEvent {
|
private static class CallEvent {
|
||||||
|
final Source source;
|
||||||
final String number;
|
final String number;
|
||||||
final long timestamp;
|
final long timestamp;
|
||||||
|
|
||||||
public CallEvent(String number, long timestamp) {
|
public CallEvent(Source source, String number, long timestamp) {
|
||||||
|
this.source = source;
|
||||||
this.number = number;
|
this.number = number;
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<string name="block_negative_sia">Блокировать по отзывам</string>
|
<string name="block_negative_sia">Блокировать по отзывам</string>
|
||||||
<string name="block_negative_sia_numbers_summary">Блокировать звонки c номеров с отрицательным рейтингом</string>
|
<string name="block_negative_sia_numbers_summary">Блокировать звонки c номеров с отрицательным рейтингом</string>
|
||||||
<string name="block_hidden_number">Блокировать скрытые номера</string>
|
<string name="block_hidden_number">Блокировать скрытые номера</string>
|
||||||
<string name="block_hidden_number_summary">(Экспериментальная функция.) Блокировать звонки со скрытых номеров. Вероятно, работает лучше в \"Продвинутом режиме блокирования вызовов\". Пожалуйста, сообщите о своём опыте использования в репозитории на GitLab</string>
|
<string name="block_hidden_number_summary">Блокировать звонки со скрытых номеров. Может работать по-другому (лучше или хуже) в \"Продвинутом режиме блокирования вызовов\"</string>
|
||||||
<string name="block_blacklisted_short">Блокировать из ЧС</string>
|
<string name="block_blacklisted_short">Блокировать из ЧС</string>
|
||||||
<string name="block_blacklisted">Блокировать из чёрного списка</string>
|
<string name="block_blacklisted">Блокировать из чёрного списка</string>
|
||||||
<string name="block_blacklisted_summary">Блокировать звонки с номеров добавленных в чёрный список</string>
|
<string name="block_blacklisted_summary">Блокировать звонки с номеров добавленных в чёрный список</string>
|
||||||
|
|
|
@ -97,7 +97,7 @@
|
||||||
<string name="block_negative_sia">Block based on rating</string>
|
<string name="block_negative_sia">Block based on rating</string>
|
||||||
<string name="block_negative_sia_numbers_summary">Block calls from numbers with negative rating (based on a community database)</string>
|
<string name="block_negative_sia_numbers_summary">Block calls from numbers with negative rating (based on a community database)</string>
|
||||||
<string name="block_hidden_number">Block hidden numbers</string>
|
<string name="block_hidden_number">Block hidden numbers</string>
|
||||||
<string name="block_hidden_number_summary">(Experimental.) Block calls from hidden numbers. Probably works better in \"Advanced call blocking mode\". Please report your experience on GitLab</string>
|
<string name="block_hidden_number_summary">Block calls from hidden numbers. May work differently (better or worse) in \"Advanced call blocking mode\"</string>
|
||||||
<string name="block_blacklisted_short">Block blacklisted</string>
|
<string name="block_blacklisted_short">Block blacklisted</string>
|
||||||
<string name="block_blacklisted">Block blacklisted numbers</string>
|
<string name="block_blacklisted">Block blacklisted numbers</string>
|
||||||
<string name="block_blacklisted_summary">Block calls from numbers added to the blacklist</string>
|
<string name="block_blacklisted_summary">Block calls from numbers added to the blacklist</string>
|
||||||
|
|
Loading…
Reference in New Issue