Android заблокированных звонков через CallScreeningService иногда звонит - PullRequest
2 голосов
/ 06 марта 2020

Я занимаюсь разработкой простого приложения для блокировки вызовов 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. Но не уверен, что здесь происходит.

...