В процессе эксплуатации мобильных приложений на платформе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда после обновления операционной системы Android (особенно до версий 9, 10 и выше) функции телефонии, которые раньше работали исправно, начинают вызывать принудительное закрытие приложения (crash). Это происходит как при попытке набора номера из интерфейса 1С, так и при входящем звонке, если в приложении реализованы обработчики событий телефонии.
В данной статье мы подробно разберем причины возникновения этой проблемы, проанализируем логи ошибок Java и выясним, какие изменения в манифесте приложения и версии платформы необходимы для восстановления работоспособности.
Рассмотрим стандартный фрагмент кода на языке 1С, который используется для инициализации звонка и подключения обработчика событий. Обычно разработчики используют объект СредстваТелефонии, как показано в примере ниже:
#Если МобильноеПриложениеКлиент Тогда
Если СредстваТелефонии.ПоддерживаетсяНаборНомера() Тогда
СтруктураЗвонка.Номер = Номер;
СредстваТелефонии.НабратьНомер(Номер, ВызватьСразу);
Если СредстваТелефонии.ПоддерживаетсяОбработкаЗвонков() Тогда
ПодпискаНаЗвонки = Новый ОписаниеОповещения("ОбработчикЗвонков", СтруктураЗвонка.Форма, Новый Структура("IDЗадачи", IDЗадачи));
СредстваТелефонии.ПодключитьОбработчикЗвонков(ПодпискаНаЗвонки);
КонецЕсли;
КонецЕсли;
#КонецЕсли
Проанализируем ситуацию: на старых версиях Android этот код работает корректно. Однако на современных устройствах вызов метода НабратьНомер или срабатывание ПодключитьОбработчикЗвонков приводит к моментальному вылету. При этом сам системный «дозвонщик» Android может начать вызов, но приложение 1С закрывается с критической ошибкой.
Первая и самая важная причина кроется в тексте ошибки, который можно увидеть в системном логе (logcat) устройства:
java.lang.SecurityException: Permission Denial: opening provider com.android.providers.contacts.CallLogProvider ... requires android.permission.READ_CALL_LOG or android.permission.WRITE_CALL_LOG
Выясним причину этой ошибки. Начиная с Android 9 (API level 28), компания Google ужесточила политику конфиденциальности. Ранее для получения номера телефона входящего или исходящего вызова было достаточно общего разрешения READ_PHONE_STATE. Теперь же доступ к информации о номерах (журналу вызовов) выделен в отдельную «опасную» группу разрешений. Если приложение пытается программно обратиться к данным вызова (даже просто получить номер, на который оно само инициировало звонок), система блокирует доступ и выбрасывает исключение SecurityException.
Вторая ошибка, которая часто сопровождает падение (особенно в версии платформы 8.3.13 и близких к ней), выглядит следующим образом:
java.lang.RuntimeException: Error receiving broadcast Intent ... Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference at com.e1c.mobile.TelephonyToolsImpl$3.onReceive
Проанализируем ситуацию: когда происходит событие изменения состояния телефона (PHONE_STATE), внутренняя библиотека мобильной платформы 1С пытается обработать входящие данные. Поскольку у приложения нет прав на чтение журнала звонков (см. предыдущий пункт), Android возвращает пустое значение (null) вместо номера телефона в целях безопасности. Программный код платформы 8.3.13 не ожидает получения null и пытается вызвать метод сравнения строк equals() для этого объекта. Это приводит к NullPointerException внутри Java-части мобильной платформы.
Для устранения данных ошибок необходимо выполнить комплекс действий, связанных как с настройкой сборки приложения, так и с обновлением технологической платформы.
В первую очередь проверим настройки в самой конфигурации. Разберем необходимые действия:
Однако стоит учитывать, что в старых версиях сборщика мобильных приложений (соответствующих платформе 8.3.13) флажок «Телефония» может не добавлять в манифест разрешение READ_CALL_LOG, так как на момент выпуска версии это не требовалось системой Android.
Если после включения всех прав в конфигурации ошибка сохраняется, необходимо при сборке приложения явно указать требуемые разрешения. Рассмотрим, как это сделать:
AndroidManifest.xml, который формируется в каталоге сборки, необходимо вручную добавить следующие строки внутри тега <manifest>:
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
Это позволит операционной системе понять, что приложение претендует на доступ к данным о вызовах. При первом запуске или при первом звонке Android предложит пользователю подтвердить эти права в явном виде.
Поскольку ошибка NullPointerException является внутренней ошибкой реализации платформы 1С (некорректная обработка отсутствующего номера), наиболее эффективным решением будет переход на более современную версию платформы.
Выясним преимущества перехода:
Рекомендация: Если ваша конфигурация позволяет, выполните сборку мобильного приложения на платформе версии не ниже 8.3.15. Это решит проблему с падением из-за null-ссылок в коде телефонии.
Мы проанализировали критическую проблему падения 1С при звонках и определили, что она носит двойственный характер: ограничение прав доступа на уровне ОС Android и программная недоработка в старой версии мобильной платформы 1С.
Для успешного решения задачи выполним следующие действия: добавим разрешения READ_CALL_LOG и WRITE_CALL_LOG в манифест приложения и обновим платформу 1С:Предприятие до актуальной версии, в которой исправлены ошибки обработки пустых значений от системных провайдеров данных. Это обеспечит стабильную работу функций телефонии и интеграции с CRM-системами на базе мобильной 1С.