Дмитрий Мячин
Вторая часть лекции рассматривает реальные проблемы на реальных устройствах. Все рассмотренные проблемы вызваны архитектурой системы, а если точнее, изменениями, которые вносят некоторые производители в архитектуру, а приложения ничего с этим сделать не могут, потому что не имеют возможности влиять на работу системы. Использовать буду только реальные ситуации и, по возможности, буду давать ссылки в баг-трекер Google, чтобы каждый мог убедиться, что это всё не выдумки.
В целом в прошлых сериях мы в общих чертах выяснили, как работают приложения в Android. Это поможет нам понять, как проводить хорошее глубокое тестирование приложений и самой операционной системы. Глубоким я буду считать такое, которое позволит находить не ошибки переводов и не проблемы с влезанием надписей в кнопки, а разломы функциональности продуктов из-за архитектурных особенностей как Android, так и устройств под этой операционной системой.
Сразу нужно уяснить, что более-менее одинаковые реализации Android есть лишь у горстки производителей. И ситуация, когда на одной и той же модели устройства, но выпущенные в разных странах или даже в одной стране, но под эгидой разных операторов, используются разные прошивки — это жестокая реальность! Прошивка — это не ОС Android, а всё, под чем работает устройство. Начиная от загрузчика, заканчивая доступными в Android на этом устройстве языками и предустановленными приложениями с их настройками. ОС Android — это лишь часть прошивки.
Все ключевые термины, что описаны ниже, вы уже встречали в предыдущей статье из этого цикла. Теперь дополним понимание этих терминов с той стороны, которая называется «исследовательское регрессионное тестирование». «Регрессионное» здесь потому, что я буду описывать проблемы, которые появлялись из-за обновлений системы.
Песочница
Так как приложения редко напрямую общаются с внешним миром, живя в своих уютных мирках, приходится каждый раз проверять всё, что вы тестировали много раз и оно никогда не ломалось. Потому что, будьте уверены, завтра прилетит обновление прошивки размером три килобайта, вносящее косметическое изменение для этого аппарата, а у вас поедут все цвета, и буквы станут размером в половину экрана.
Больше того, у приложения могут отвалиться некоторые типы доступов (скажем, на чтение останется, а на запись — нет), приложение может перестать загружаться после перезагрузки, приложение может начать зависать, приложение может даже полностью «забывать» все свои настройки на каждом старте.
Это я не утрирую, не шучу. На устройства от Samsung, во времена Android 2.2, прилетело обновление прошивки, а обновление прошивки — всегда хорошо, конечно. Но производитель допустил критическую ошибку, и у всех приложений, которые не были системными, стали «теряться» настройки, как те, что выставил пользователь, так и настройки по умолчанию. То есть все приложения успешно запускались, распаковывались, инициализировались, подгружали стартовые настройки, подчинялись этим настройкам, сохраняли их и работали. Пользователи взаимодействовали с приложениям, вносили изменения в настройки и всё было хорошо. Но в какой-то момент (обычно на перезагрузке, но и просто при срабатывании системного киллера) эти настройки просто терялись. При запуске приложение было будто только что установлено и весь процесс первого запуска повторялся вновь. Разработчикам пришлось работать с этим, ставя разные костыли, хранить настройки в двух разных местах. А куда деваться? Samsung — крупнейший производитель устройств под Android. Не поддерживать устройства от Samsung — потерять почти весь рынок.
Но это был старый пример, из уже далёкого прошлого. А вот более новый и ещё более глобальный. В Android 5.1 (на тот момент это была самая последняя версия ОС) у приложений была прямая возможность понимать, на какой экран сейчас смотрит пользователь, то есть активити какого приложения в каждый момент времени на переднем плане. Работала она ещё со времён Android 4.0 или около того, а потому активно использовалась различными приложениями, которым нужно было такое понимание. Работала, пока те, кто отвечает за безопасность Android в стенах Google, не хлопнули себя по лбу со словами: «Коллеги, да это же не безопасно!».
Здесь нужна небольшая справка. Начиная с августа 2015 года Google перешёл на процедуру ежемесячного выпуска исправлений безопасности. Следить за ними вы можете здесь или здесь. Эти патчи не поднимают версию Android (то есть версия 5.1 остаётся версией 5.1, а не становится 5.1.1), их можно увидеть в разделе информации о системе. Там есть отдельный пункт Android Security Patch Level, где будет написано что-то типа 1-ое марта 2016 года.
Вернёмся к повествованию. В Google хлопнули себя по лбу и в очередном патче безопасности взяли и запретили использование этой возможности. Итог — сломалась работа всех приложений, которым она была нужна, и которые работали уже несколько лет без изменений в этой части кода. Разработчики начали связываться с Google и сообщать им о проблеме, потому что это было похоже на баг. Но нет, Google ответил, что это запланированное изменение.
Те производители, для которых репутация очень важна, вынуждены были задать максимальную версию API так, чтобы запретить установку на Android 5.1 до момента, когда найдётся обходное решение.
Это были примеры, когда обновление прошивки без изменения версии Android приводило к критичным проблемам. А вот пример глобального изменения в Android, который затронул очень много разных приложений, и при этом был явно продума не сильно — это обработка SMS в Android 4.4 и выше.
До Android 4.3 включительно любое приложение, если у него были необходимые права, могло отправлять SMS, могло читать SMS из специального общесистемного хранилища, могло даже вносить изменения в это общесистемное хранилище SMS. Как правило, модификация использовалась для скрытия или добавления SMS без реальной отправки. Например, сторонние SMS-клиенты могли копировать все сообщения себе и удалять их в общесистемном. Тогда другие приложения не могли прочесть их, ведь из своих песочниц они не могли достучаться до данных, скажем, данных приложения Go SMS Pro. Приложение «Kaspersky Internet Security для Android» использовало модификацию для компонентов «Личные контакты», «Антивор» и «Антиспам».
Компонент «Личные контакты» скрывал какие-то личные данные, если пользователь этого желал. Например, у пользователя есть бизнес-партнёр и он опасается, что кто-то может любым способом считать переписку с партнёром, отследить звонки или вообще получить его контакты. Пользователь мог внести контакт партнёра в список контроля и тогда антивирус удалял его из системного списка контактов (вспоминаем «контент-провайдеры»), удалял всю SMS переписку с ним из общесистемного хранилища (тоже контент-провайдер), удалял лог звонков (и снова контент-провайдер). Перед удалением антивирус, конечно, копировал всё к себе. В итоге, даже если телефон попадал в руки злоумышленника, даже если в телефоне был необнаружаемый троян, он не мог получать данные от контент-провайдеров, потому что данных уже нет, а от антивируса он не мог получить, потому что, во-первых, из своей песочницы не мог прочесть файлы антивируса, во-вторых, сам антивирус не предоставлял возможности запросить эти данные, кроме как изнутри себя самого. Но, даже чтобы восстановить данные обратно, нужно ввести секретный код, который устанавливал сам пользователь.
«Антивор» удалял управляющие SMS и ответные SMS. Управляющие — это те, где были команды антивора (обнаружить телефон, включить сирену), а ответные — это результат выполнения команды (успех или провал). Удалять их было нужно потому, что в управляющих сообщениях был секретный код, а сам факт наличия ответных SMS говорил бы вору, что за телефоном следят.
«Антиспам» и «Антифишинг» — удаляли входящие SMS, если те были не нужны пользователю, если они были, соответственно, спам-рассылками или содержали фишинговые ссылки.
Безусловно, такие широкие возможности работы с SMS — это небезопасно. Троянские приложения без проблем читали SMS от банков, в которых написаны одноразовые коды для двухфакторной аутентификации и сразу отправляли эти SMS своим владельцам. Также они могли удалить своё исходящее сообщение и входящее от банка. Тогда пользователю казалось, что банк просто ещё не прислал код и нужно ещё подождать.
Разумнее всего здесь было бы запретить читать SMS всем приложениям, которые не были бы однозначно доверенными. Вместо этого в Android 4.4 и выше Google запретил модификацию системного хранилища. То есть любое приложение, при наличии прав, по-прежнему может читать и отправлять любые сообщения, но теперь не может их скрыть. Модификация теперь разрешена только тому приложению, которое пользователь выбирает SMS клиентом по умолчанию. Итог — трояны по-прежнему могут читать временные пароли из SMS, по-прежнему могут их отправлять своим хозяевам, а вот приложения, созданные для защиты, больше не могут работать как раньше.
Разрешения
До Android 6 все разрешения (permissions) приложений показывались один раз при установке его из Play Market. Или, если пользователь устанавливал приложение не из магазина, на специальном экране. Кстати, это тот случай, когда локальная установка безопаснее установки из магазина приложений. Дело в том, что активити, на котором были перечислены все требования приложения, начиная с Android 4.2 красиво подсвечивало опасные разрешения типа «приложение может совершать звонки» и «приложение может отправлять SMS». Кроме того кнопку установки нажать нельзя, пока не прокрутишь весь список разрешений до конца.
Почему эти разрешения вообще показывались? Дело в том, что архитектура всех версий Android, даже 6.0, при определённых условиях, была такова, что, во-первых, о разрешениях только предупреждалось, но управлять ими пользователь не мог, во-вторых, приложение могло выполнять объявленные действия в любой момент. То есть вредоносное приложение реально могло определять, что устройство бездействует, и экран выключен, и начать звонить на премиум-номера с тарификацией по куче денег в секунду. Раньше они ещё и SMS отправляли на специальные номера, подписывая пользователей на всякие платные рассылки, но в Android 4.2 Google прикрыл эту лавочку. Если у вас есть устройство на Android 4.2 и выше, которое может отправлять SMS, вы можете взять его в руки и попытаться отправить SMS на любой премиумный номер, например на 4444. Даже если это системный SMS-клиент — не важно, вы убедитесь, что SMS не будет отправлено до тех пор, пока вы явным образом не дадите согласия на это.
И здесь некоторые разработчики ударились о подводный камень, которого ранее не было. Если их приложение использовало короткие номера для работы своего сервиса совершенно легально, нужно было объяснять каждому клиенту, как разрешить отправку таких SMS. Не стоит думать, что речь только про подписки на анекдоты. Есть действительно полезные сервисы, а оплата таких номеров ложится на поставщика услуг (который взимает плату за них через общую стоимость продукта, понятное дело). Впрочем, многие производители просто сменили номера, избавившись от коротких.
Так вот, такая модель поведения была до Android 6.0, но в самом 6.0 «мир изменился». Начиная с этой версии, Google внедрил систему динамического запроса разрешений. Теперь разрешения делятся, с точки зрения Google, на опасные и не опасные. Не опасные по умолчанию разрешены и запретить их для приложения пользователь не может. Например, нельзя заблокировать приложению выход в Интернет, избежав показа рекламы (выход в Интернет входит в группу неопасных разрешений). Но можно запретить приложению получить уникальный идентификатор устройства (получение обезличенного ID-устройства входит в группу опасных разрешений). Думаю, не стоит объяснять, почему Google не даёт запрещать показывать рекламу в приложениях.
Так как новая политика сильно отличается от политики предыдущих версий Android, Google дал производителям время на переход на новую модель, оставив обратную совместимость. Работает она следующим образом. Если в манифесте приложения объявлено, что минимальная версия API или же Target-версия, если таковая задана, ниже чем 23 (это и есть Android 6.0), то приложение считается устаревшим. Для него все разрешения, включая опасные, по умолчанию включены. Play Market, как и раньше, показывает список всех разрешений один раз при нажатии на кнопку «Установить». Однако пользователь всё равно может запретить опасные разрешения, когда установит приложение. Система предупредит пользователя, что приложению может быть не готово к такому, но не запретит снятие прав. Если приложение уже было запущено и работало в этот момент, система услужливо его «пристрелит». Для самого же приложения система будет возвращать стандартные пустые ответы на заблокированные запросы. Как правило, приложения готовы к таким ответам, потому что ситуация, когда при запросе списка контактов система возвращает пустой ответ вполне допустима.
Если же минимальная или Target-версия API будет 23 и выше, то для приложения включится динамический запрос разрешений. Play Market не будет показывать список разрешений. По умолчанию все опасные permissions будут отключены. Система будет спрашивать пользователя, можно ли использовать такой-то приложению или нет только в момент, когда приложение его запросит (то есть если разработчик не реализовал запрос, то получить разрешение он не сможет). Если пользователь запретит использование запрошенного разрешения, то приложение получит SecurityException.
Много падений словили разработчики разных приложений, потому что многие были готовы получать пустые списки, пустые объекты, NullPointerExeption’ы на запрос «дай мне список контактов», но никто не был на тот момент готов на новый SecurityException.
Выходит, тестировщик должен знать, какие разрешения и для чего именно нужны приложению и проверять поведение, когда пользователь даёт разрешение, даёт разрешение, а потом блокирует его в настройках, не даёт разрешения, не даёт разрешения и ставит галку «больше не спрашивать» (она становится доступна на второй попытке запроса разрешения), то есть вечный запрет. Без понимания, для чего каждый из «пермишенов» нужен приложению, нельзя будет эффективно протестировать его. Одно дело лезть сразу в нужные места с конкретной целью, другое дело — тыкать во всё, что можно. Ну и, конечно, нужно читать гайдлайны, чтобы можно было спорить, какие из разрешений нужно запрашивать и когда, чтобы не раздражать пользователя. Потому что у некоторых разработчиков есть очень плохой стиль разработки — запросить сразу всё «на всякий случай», даже если часть возможностей из предоставленных никогда не будут использованы.
Продолжение следует.