Я занимаюсь разработкой простого приложения для блокировки вызовов Android и довольно успешно использовал CallScreeningService. Я выполняю некоторые вызовы базы данных, чтобы обнаружить блокируемые номера, и запускаю задачу asyn c, чтобы отправить некоторую информацию о заблокированных вызовах на внешний сервер. Реализация работает нормально большую часть времени. Но иногда это с треском проваливается, заставляя телефон звонить, даже если говорят, что звонок заблокирован. Ниже приведен мой код.
@RequiresApi(api = Build.VERSION_CODES.N)
public class CallCaptureService extends CallScreeningService {
@Override
public void onScreenCall(Call.Details details) {
boolean blockCall = false;
String handle = details.getHandle().toString();
handle = handle.replace("tel:", "");
Log.d(SettingsConstants.CALL_LOG_TITLE, String.format("Call event received. Handle='%s'", handle));
long startTime = System.currentTimeMillis();
if (handle != null && !handle.isEmpty()) {
try {
String callingNumber = URLDecoder.decode(handle, "UTF-8");
if (AppUtil.isBlockedNumber(callingNumber)) { //This is a sqlite database call
blockCall = true;
} else if (AppUtil.isKnownPhoneNumber(this, callingNumber)) { //This is again a database call
//Do not block call
}
Log.i(SettingsConstants.CALL_LOG_TITLE, String.format("Sending call information to Call handler"));
Intent callCapturedIntent = new Intent();
callCapturedIntent.putExtra(ExtraKeys.CALLING_NUMBER_EXTRA_KEY, callingNumber);
callCapturedIntent.putExtra(ExtraKeys.CALL_STATE_EXTRA_KEY, CallState.RINGING.getValue());
callCapturedIntent.putExtra(ExtraKeys.CALL_SCREENING_USED_EXTRA_KEY, true);
callCapturedIntent.putExtra(ExtraKeys.ALREADY_KNOWN_NUMBER_EXTRA_KEY, isKnownPhoneNumber);
handleCallForCallScreening task = new handleCallForCallScreening(callCapturedIntent, getApplicationContext());
task.execute();
CallResponse.Builder response = new CallResponse.Builder();
response.setRejectCall(blockCall);
response.setSkipCallLog(blockCall);
response.setSkipNotification(blockCall);
response.setDisallowCall(blockCall);
long endTime = System.currentTimeMillis();
long delay = (endTime - startTime);
Log.i(SettingsConstants.CALL_LOG_TITLE, String.format("(Waiting for call) time for processing %s = %d", handle, delay));
if (blockedCallHangupTime > 0) {
long remainingTime = blockedCallHangupTime - delay;
if (remainingTime > 0) {
try {
Log.i(SettingsConstants.CALL_LOG_TITLE, String.format("Sleeping for %d (ms)", remainingTime));
Thread.sleep(remainingTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
respondToCall(details, response.build());
if (blockCall) {
Log.i(SettingsConstants.CALL_LOG_TITLE, String.format("Call Blocked Successfully"));
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
Log.d(SettingsConstants.CALL_LOG_TITLE, String.format("Finished handling call event. Handle='%s'", handle));
}
class handleCallForCallScreening extends AsyncTask<Void, Void, Void> {
Context _ctx;
Intent _intent;
public handleCallForCallScreening(Intent intent, Context context) {
_ctx = context;
_intent = intent;
}
@Override
protected Void doInBackground(Void... voids) {
CallHandlerHelper.handleCall(_intent, _ctx); //This method has a network operation, which will call a rest API from a remote server
return null;
}
}
}
И это фрагмент кода метода в нашей задаче asyn c. Он содержит сетевую операцию.
public static void handleCall(@NonNull Intent intent, Context context) {
...
String callingNumber = intent.getStringExtra(ExtraKeys.CALLING_NUMBER_EXTRA_KEY);
boolean callScreeningUsed = intent.getBooleanExtra(ExtraKeys.CALL_SCREENING_USED_EXTRA_KEY, false);
boolean isKnownPhoneNumber = intent.getBooleanExtra(ExtraKeys.ALREADY_KNOWN_NUMBER_EXTRA_KEY, false);
CallState state = CallState.fromInteger(intent.getIntExtra(ExtraKeys.CALL_STATE_EXTRA_KEY, 3));
...
CallSenderResult result = sendCALL(context, callingNumber, requestId, null);
}
private static CallSenderResult sendCALL(Context ctx, String callingNumber, String requestId) {
String appInstanceUniqueKey = StorageHelper.getAppInstanceUniqueKey(ctx);
CallSenderResult result = new CallSenderResult();
try {
if (SettingsConstants.IGNORE_SSL_CERTIFICATE) {
CommunicationRest.disableSSLCertificateChecking();
}
String accessToken = StorageHelper.getAccessToken(ctx);
String phoneNumberKey = StorageHelper.getPhoneNumberKey(ctx);
String refreshToken = StorageHelper.getRefreshToken(ctx);
JSONObject jsonObject = new JSONObject();
jsonObject.put("callingNumber", callingNumber);
jsonObject.put("requestId", requestId);
jsonObject.put("phoneNumberKey", phoneNumberKey);
jsonObject.put("detectedCallingNumbers", detectedCallingNumbersString);
....
Log.i(SettingsConstants.CALL_LOG_TITLE, String.format("Sending CALL information. CallingNumber:'%s', RequestId:'%s', delectedCallingNumbers:'%s'", callingNumber, requestId, detectedCallingNumbers));
String jsonString = jsonObject.toString();
HttpsURLConnection conn = NetworkUtil.getHttpPostURLConnectionForJson(accessToken, jsonString, FirebaseConstants.CALL_RESPONSE_URL);
int responseCode = conn.getResponseCode();
switch (responseCode) {
case 200:
Log.i(SettingsConstants.CALL_LOG_TITLE, String.format("CALL response sent successfully. ReqId:'%s', CallingNumber:'%s', delectedCallingNumbers:'%s'", requestId, callingNumber, detectedCallingNumbers));
result.setState(CallSenderState.SUCCESS);
break;
...
}
} catch (Exception e) {
...
}
return result;
}
public static final int CONNECTION_TIMEOUT_IN_MS = 30 *1000;
public static final int READ_TIMEOUT_IN_MS = 30 *1000;
public static HttpsURLConnection getHttpPostURLConnectionForJson(String accessToken, String json, String urlString) throws IOException {
URL url = new URL(urlString);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
conn.setRequestProperty("Accept", "*/*");
conn.setRequestProperty("authorization", "Bearer " + accessToken);
conn.setDoOutput(true);
conn.setConnectTimeout(SettingsConstants.CONNECTION_TIMEOUT_IN_MS); //
conn.setReadTimeout(SettingsConstants.READ_TIMEOUT_IN_MS);
OutputStream os = conn.getOutputStream();
os.write(json.getBytes("UTF-8"));
os.close();
return conn;
}
Этот код работает нормально большую часть времени и блокирует вызовы, если номер найден в нашей базе заблокированных номеров. Но иногда это не удается, даже заблокированный номер найден в нашей базе данных, и в журнале говорится, что номер успешно заблокирован.
Это извлечение журнала из нашего устройства, когда номер предназначен для блокировки, но фактически звонил.
2020-03-06 13:15:32.079 DEBUG CALL: Call event received. Handle='%2B85590914400'
2020-03-06 13:15:32.091 INFO CALL: Sending call information to Call handler
2020-03-06 13:15:32.099 INFO CALL: (Waiting for call) time for processing %2B85590914400 = 14
2020-03-06 13:15:32.116 INFO CALL: Sending CALL information. CallingNumber:'+85590914400' // This is printed from async task
2020-03-06 13:15:37.137 INFO CALL: Call Blocked Successfully
2020-03-06 13:15:37.162 DEBUG CALL: Finished handling call event. Handle='%2B85590914400'
2020-03-06 13:15:37.240 INFO CALL: Phone State change detected.(RINGING) //This is where the ringing occurs.
2020-03-06 13:15:37.890 INFO CALL: CLI response sent successfully. ReqId:'1583480726222', CallingNumber:'+85590914400', delectedCallingNumbers:'null' // This is printed from async task
2020-03-06 13:15:38.941 INFO CALL: Phone State change detected.(IDLE)
Одна вещь, которую я заметил, заключается в том, что между 5 "существует задержка в 5 с" ( Ожидание вызова) время обработки ... "сообщения и сообщения" вызов заблокирован успешно ". В ситуациях, когда этот код работает отлично, промежуток времени между этими двумя сообщениями составляет всего несколько миллисекунд. Поэтому я сомневаюсь, что внутри сообщения responseToCall происходит какое-то зависание.
Также эта проблема чаще всего возникает, когда у меня слабое соединение для передачи данных. Но сетевая операция записана внутри асинхронной задачи c, которая может не влиять на поведение CallScreeningService. Но не уверен, что здесь происходит.