14 апреля 2016 г.

Исследовательское тестирование как метод изучения Android

Картинка без смысла
    Вторая часть лекции рассматривает реальные проблемы на реальных устройствах. Все рассмотренные проблемы вызваны архитектурой системы, Если точнее, изменениями, которые вносят некоторые производители в архитектуру, а приложения ничего с этим сделать не могут, потому что не имеют возможности влиять на работу системы. Использовать буду только реальные ситуации и, по возможности, буду давать ссылки в баг-трекер Google, чтобы каждый мог убедиться, что это всё не выдумки.

    В целом мы выяснили в общих чертах, как работают приложения в Android. Это поможет нам понять, как проводить хорошее глубокое исследовательское тестирование приложений и самой операционной системы. Глубоким я буду считать такое, которое позволит находить не ошибки переводов и не проблемы с влезанием надписей в кнопки, а разлом функциональности продуктов из-за архитектурных особенностей как Android, так и устройств под этой операционной системой.

    Сразу нужно уяснить, что более-менее одинаковые реализации Android есть лишь у горстки производителей. И ситуация, когда на одной и той же модели устройства, но выпущенные в разных странах или даже в одной стране, но от разных операторов, используются разные прошивки — это жестокая реальность! Прошивка — это не ОС Android, а всё, под чем работает устройство. Начиная от загрузчика, заканчивая доступными в Android на этом устройстве языками и предустановленными приложениями с их настройками. ОС Android — это лишь часть прошивки.

    Все ключевые термины, что описано ниже, вы уже встречали в предыдущей статье из этого цикла. Теперь дополним понимание этих терминов с той стороны, которая называется «исследовательское регрессионное тестирование». Регрессионное здесь — потому что я буду описывать проблемы, которые появлялись из-за обновлений системы.


Песочница

    Так как приложения редко напрямую общаются с внешним миром, живя в своих уютных мирках, приходится каждый раз проверять всё, что вы тестировали много раз и оно никогда не ломалось. Потому что, будьте уверены, завтра прилетит обновление прошивки размером три килобайта, вносящее косметическое изменение для этого аппарата, а у вас поедут все цвета и буквы будут размером в половину экрана.

    Больше того, у приложения могут отвалиться некоторые типы доступов (скажем, на чтение останется, а на запись — нет), приложение может перестать загружаться после перезагрузки, приложение может начать зависать, приложение может даже полностью «забывать» все свои настройки на каждом старте.
    Это я не утрирую, не шучу. На устройства от Samsung, во времена Android 2.2, прилетело обновление прошивки, а обновление прошивки — всегда хорошо, конечно. Но производитель допустил критическую ошибку и у всех приложений, которые не были системными, стали «теряться» настройки, как те, что выставил пользователь, так и настройки по умолчанию. То есть все приложения успешно запускались, распаковывались, инициализировались, подгружали стартовые настройки, подчинялись этим настройкам, сохраняли их и работали. Пользователи взаимодействовали с приложениям, вносили изменения в настройки и всё было хорошо. Но в какой-то момент (обычно на перезагрузке, но и просто при срабатывании системного киллера) эти настройки просто терялись. При запуске приложение было будто только что установлено и весь процесс первого запуска повторялся вновь. Разработчикам пришлось работать с этим, ставя разные костыли, хранить настройки в двух разных местах. А куда деваться? Samsung — крупнейший производитель устройств под Android. Не поддерживать устройства от Samsung — потерять почти весь рынок.

    Но это был старый пример, из уже далёкого прошлого. А вот более новый и ещё более глобальный. В Android 5.1 (на тот момент это была самая последняя версия ОС) у приложений была прямая возможность понимать, на какой экран сейчас смотрит пользователь, то есть активити какого приложения в каждый момент времени на переднем плане. Работала она ещё со времён Android 4.0 или около того, а потому активно использовалась различными приложениям, которым нужно было такое понимание. Работала, пока те, кто отвечает за безопасность Android в стенах Google, не хлопнули себя по лбу со словами: «Коллеги, да это же не безопасно!».
    Здесь нужна небольшая справка. Начиная с августа 2015 года Google перешёл на процедуру ежемесячного выпуска исправлений безопасности. Следить за ними вы можете здесь: https://source.android.com/security/bulletin/ или здесь: https://groups.google.com/forum/#!forum/android-security-updates. Эти патчи не поднимают версию Android (то есть версия 5.1 остаётся версией 5.1, а не становится 5.1.1), их можно увидеть в разделе информации о системе. Там есть отдельный пункт Android Security Patch Level, где будет написано что-то типа 1-ое марта 2016 года:
Обновление безопасности от 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» использовалj модификацию для компонентов «Личные контакты», «Антивор» и «Антиспам».
    Компонент «Личные контакты» скрывал какие-то личные данные, если пользователь этого желал. Например, у пользователя есть бизнес-партнёр и он опасается, что кто-то может любым способом считать переписку с партнёром, отследить звонки или вообще получить его контакты. Пользователь мог внести контакт партнёра в список контроля и тогда антивирус удалял его из системного списка контактов (вспоминаем контент провайдеры), удалял всю 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 на короткий номер...


...провалилась
     И здесь некоторые разработчики ударились о подводный камень, которого ранее не было. Если их приложение использовало короткие номера для работы своего сервиса совершенно легально, нужно было объяснять каждому клиенту, как разрешить отправку таких 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 не будет показывать список разрешений. По умолчанию все опасные пермишены будут отключены. Система будет спрашивать пользователя, можно ли использовать эту такой-то пермишен приложению или нет только в момент, когда приложение его запросит (то есть если разработчик не реализовал запрос, то получить разрешение он не сможет). Если пользователь запретит использование запрошенного разрешения, то приложение получит SecurityException.
    Много падений словили разработчики разных приложений, потому что многие были готовы получать пустые списки, пустые объекты, NullPointerExeption'ы на запрос «дай мне список контактов», но никто не был на тот момент готов на новый SecurityException.

    Выходит, тестировщик должен знать, какие разрешения и для чего именно нужны приложению и проверять поведение, когда пользователь даёт разрешение, даёт разрешение, а потом блокирует его в настройках, не даёт разрешения, не даёт разрешение и ставит галку «больше не спрашивать» (она становится доступна на второй попытке запроса разрешения), то есть вечный запрет. Без понимания, для чего каждый из пермишенов нужен приложению, нельзя будет эффективно протестировать его. Одно дело лезть сразу в нужные места с конкретной целью, другое дело — тыкать во всё, что можно. Ну и, конечно, нужно читать гайдлайны, чтобы можно было спорить, какие из пермишенов нужно запрашивать и когда, чтобы не раздражать пользователя. Потому что у некоторых разработчиков есть очень плохой стиль разработки — запросить сразу всё «на всякий случай», даже если часть возможностей из предоставленных никогда не будут использованы.

Изменения в API новых версий Android

    Каждая новая версия Android достаточно неплохо описывается в официальной документации и почти всегда можно понять, что же задел Google и куда смотреть в первую очередь. Но если бы всё было так просто. Google вносит изменения в API, исправляя ошибки или внедряя новые возможности, запрещая старые. Каждый из этих вариантов — головная боль. И ладно бы, когда при блокировании старых возможностей их сначала объявляли устаревшими и не рекомендуемыми к использованию (так называемые «депрекейтед» (deprecated)), но реальная блокировка была бы только в X+1 версии… Но об этом позже, сначала расскажу об улучшениях и исправлениях ошибок в тех местах, которые не трогали годами.

    В Android до 5.0 была своеобразная ошибка. Приложение, если оно имеет определённые, расширенные права, так называемые права Администратора устройства (Device Admin), может ставить свой пароль блокировки устройства. Это очень полезно для приложений-антиворов. Можно отправить команду и устройство будет заблокировано тем паролем, который пользователь пожелает. Отключить парольную защиту тоже легко — для этого нужно передать  системе пустой пароль. То есть если передать не пустую строку, то паролем будет эта строка. Если передать пустую строку, то парольная защита вообще отключается. В Android 5.0 сделали мелкое улучшение, разрешив передавать не только строку, но и null...

    И вы бы знали, сколько часов я потратил на тестирование исправления ошибки, которая возникла после этого мелкого улучшения и есть до сих пор только в Android 5.0. В Android 4.4 её ещё нет, а в Android 5.1 её уже нет. При внесении улучшения в API была допущена серьёзная ошибка в другом месте в Android, из-за которой пустой пароль, не важно, пустая ли это строка или null, ставился именно как пароль. То есть раньше это вызывало отключение парольной защиты, а в Android 5.0 пароль устанавливался в «ничего».
    Пользователь по-прежнему видел экран блокировки, вводил любые пароли, которые он мог бы поставить в здравом уме, но не мог пройти дальше, потому что всё время получил от экрана блокировки ответ, что пароль неправильный. Потому что правильный пароль — пустой. Нужно было нажать Enter, не вводя вообще ничего! Затем открыть настройки системы, найти там пункт блокировки экрана, где было написано, что блокировки нет, и выбрать «Без блокировки» ещё раз. И только тогда парольная защита на самом деле отключалась. Маленькое улучшение, а столько проблем.

    Это что касается изменений того, что работало всегда. Конкретно отключение блокировки путём передачи пустого пароля работало, начиная с Android 2.2, где вообще появилась эта возможность, сломалась строго в 5.0 и была починена в 5.1. Между 2.2 и 5.0 было почти 5 лет правильной работы. Но бывает ещё внезапное удаление некоторых возможностей. Обращаю внимание, что я говорю не о нормальном поведении, когда заранее, минимум за один релиз, предупреждают, что такая-то возможность теперь деприкейтед и ищите пути обхода проблем, которые будут вызваны, когда мы вообще её совсем удалим. Я говорю именно о внезапном изменении поведения системы.

    В Android до версии 6.0 было общесистемное хранилище закладок (или букмарков, называйте как вам удобнее). Например, вы могли написать свой браузер и подгружать закладки других браузеров через контент провайдер, если другие браузеры использовали это общесистемное хранилище. Вы могли только читать общие закладки, могли делиться своими, могли вообще игнорировать общесистемное хранилище — как угодно. Скажем, Chrome и обычный браузер, предустановленный в Samsung, HTC, Motorola и прочее, использовали это общесистемное хранилище. Могли вы и два хранилища использовать — своё изолированное и общесистемное. В общесистемном хранить ссылки на сайт Русской православной церкви, а в изолированном — ссылки на двач.
    Одна из прелестей этого хранилища букмарков была в том, что все сайты, по которым гуляет пользователь, если это не режим Инкогнито, попадали туда. Это не означало, что зайдя в список закладок браузера пользователь увидел бы тысячи сайтов. Это хранилище использовалось ещё и для истории, и сайты эти он увидел бы в истории браузера. Ещё они использовались браузерами для выборки сайтов, которые они показывают на экране быстрого доступа. А так как туда попадали все сайты, то всем антивирусным компаниям было очень удобно обращаться к хранилищу, чтобы искать в нём фишинговые и вредоносные сайты. Был и метод замены букмарков, потому антивирусы могли заменить адрес сайта на свой. Обычно это был адрес страницы блокировки. Этот тип защиты называли «защита на букмарках», хотя в реальности это была «защита на истории посещения сайтов».
    Всё было здорово, пока Google не выпустил предварительные версии Android 6 (точнее Android M), в котором сломалось много чего привычного. Подавляющее большинство из сломанного было задокументировано самим Google заранее и были предложены альтернативные решения. Но вот защита на букмарках просто сломалась без предупреждения.
    Разработчики защитного ПО искали пути решения, но всё было бесполезно. Всё было разломано до основания. Конечно, в какой-то момент обратились в Google с описанием проблемы. Ребята отреагировали на него, их менеджер отписал, что проблему они у себя тоже воспроизвели. А потом, через несколько недель, незадолго до полноценного релиза Android 6, этот же менеджер внезапно написал, что это, оказывается, запланированное поведение. Ждите, скоро мы это задокументируем.
    Если вы посмотрите на последние версии защитного ПО, то заметите, что почти все они просят доступ к так называемым «Специальным возможностям». Это набор API, который был создан для разработчиков, помогающих людям с ограниченными возможностями работать с устройством. Через эти «специальные возможности», к примеру, приложения могут читать тексты на экране, понимать, на какие элементы интерфейса тапает пользователь и всё такое прочее. Если вы когда-нибудь включали Talk Back, то это пример приложения, использующего эти API. Вот разработчики защитного ПО теперь запрашивают доступ к этим возможностям, чтобы приложение могло видеть адреса сайтов (и не только) и управлять ими.

Зоопарк устройств и их прошивок

    Говорят, что фрагментация устройств и прошивок — это главная проблема именно Android. Ведь для тестирования под, скажем, iOS, нужно иметь пару устройств — iPhone и iPad. Вот если кто-то говорит подобное, будьте уверены — человек мало понимает в тестировании. Потому что для хорошего тестирования под iOS нужно iPhone и iPad всех моделей. На каждой из этих моделей нужно иметь текущий релиз операционной системы и текущий минус один, минус два, а то и минус три, если текущая вышла вот только-только. Например приложения, ориентированные на детей, тестируют и на iPhone 4 и даже на iPad первом. Просто потому что родители очень часто отдают детям свои старые устройства, а не покупают им самые последние модели.
    При этом недостаточно иметь разные устройства с разной версией iOS, нужно иметь одинаковые устройства с разной версией, так как бывают ошибки, специфичные для конкретной этой модели процессора под конкретно этой версией операционной системы! Также иногда нужно иметь несколько джейлбрейкнутых устройств для специфичных сценариев. Это, я ещё не сказал про то, как реализована поддержка внешних устройств в iOS. В Android подключение аппаратной клавиатуры зачастую можно даже не тестировать — всё проходит настолько гладко, что создаётся впечатление, что иначе в мире и быть не может. А вот в iOS ситуация, когда с внешней официальной клавиатуры отваливается ввод данных в вашем приложении — это реальная ситуация, с которой столкнулись разные разработчики.

    В общем, я уверяю вас, слишком большое разнообразие устройств и прошивок — это проблема и Android, и iOS. Ещё и у Windows Phone есть эта проблема, но мир сейчас таков, что проблема у WinPhone есть, а самого Windows Phone практически нет.

    Более того, Google набила много шишек и теперь приложения под Android максимально адаптивны. Я уже рассказывал про ресурсы приложений и это лишь малая часть адаптивности. Есть ещё такие слова, как «фрагменты» (это на уровне ОС), «гайдлайны» и «бест практикис» (уровень рекомандаций Google) и подобное. Что такое гайдлайны и бест практикс многим понятно. А «фрагменты», если говорить простым языком, это то, что нашлёпывается на активити, чтобы на устройствах с разным размером экрана всё выглядело не мерзко.
    Фрагменты появились как раз во времена Android 3.0, то есть первого Android, сделанного специально для планшетов. Вы задаёте некоторые правила поведения и Android берёт на себя перетасовывание интерфейса так, чтобы он нормально выглядел на любом устройстве с любым разрешением и любой ориентацией экрана — от телефона с экраном в 4", то телевизора.
Возможно кто-то знает, что такое Wandboard
     Это такие виджеты, если угодно. Примерно как сайты с адаптивным дизайном перетасовывают интерфейс, когда вы меняете размеры браузера, вот так и ведёт себя приложение в Android, если разработчик с умом использовал фрагменты. Ещё фрагменты могу переживать перевороты устройства. Если ориентация экрана сменилась, то активити умирает (хотя это поведение можно изменить при желании), а фрагменты могут жить. Восстанавливать состояние, если быть точным. Я напомню об этом, когда буду рассказывать о негативных сценариях в самом конце этого цикла статей.

    В чём реальная проблема Android — так в том, что исходный код AOSP открыт и у производителей есть возможность вносить в него изменения. Здесь стоит отвлечься, чтобы ввести ещё одно базовое понятие — «кастомная прошивка». Обычно под кастомными прошивками понимают всякие любительские модификации более-менее официальных прошивок, а также Cyanogen, MIUI, Flyme OS и подобные. Называть Cyanogen кастомной прошивкой так привыкли, что у многих из тех, с кем я общался, не укладывается в голове, что абсолютно все прошивки для абсолютно всех устройств являются кастомными!
    Не кастомным является только AOSP. AOSP — это Android Open Source Project. Это тот самый ванильный Android. В нём нет, не было и, надеюсь, не будет никаких приложений от Samsung, от HTC, ASUS и даже фирменных приложений от Google. В нём нет Chrome, нет Google Play Services, Play Market, GMail, Hangouts, Inbox и всего такого. И ещё в нём нет драйверов для всего многообразия устройств, на которых установлен Android.
    Чтобы AOSP не просто запустился, а ещё полноценно поддерживал любое устройство, в него нужно внедрить драйверы компонентов устройства, нужно будет поставить костыли для драйверов (в этом мире ничего не работает просто так!), сделать дополнительные настройки. В итоге мы получаем кастомизированный AOSP. Кастом в чистом виде. Другими словами на Nexus устройствах также установлена «кастомная прошивка» на основе Android. Google взял AOSP и вкорячил в него, помимо драйверов, свои сервисы, приложения, сделал необходимые настройки для нормальной производительности на конкретном железе. И тоже самое делают все другие производители прошивок.

    Потому давайте отделим официальные прошивки от любительских. Cyanogen в чистом виде — это отличная прошивка, но, к сожалению, она поддерживает не так много устройств, как заявлено. Поддержка большинства устройств делается любителями с низким уровнем подготовки. А возьмите в руки, скажем, устройство One Plus One. CyanogenOS для него — официальная прошивка и она работает она превосходно. Потому что заточена только на этот аппарат. На самом деле просто невозможно хорошо поддерживать весь тот зоопарк, на котором запускают CyanogenMod, потому что подавляющее большинство производителей устройств (и компонентов устройств!) не выпускает драйверы для тех версий Android, которые на эти устройства не выходили. Вот и приходится энтузиастам использовать бинарники от старых версий Android и пытаться заставить их работать на новых ну хоть как-то. «В этой прошивке работает почти всё, только блютуз не включается и вай-фай зависает». Знакомо?

    Возьмите производителя типа Samsung. У него тысячи разных моделей. Более того, одна и та же модель в разных странах — это РАЗНЫЕ модели с разной аппаратной начинкой и разными прошивками! В итоге вы можете вдоль и попрёк протестировать приложение на Galaxy S5, выпущенном для России, а после релиза получить множество отзывов о нестабильной работе на Galaxy S5, который был выпущен для Бразилии.

    Производители берут AOSP и начинают его ломать, внося «непоправимые улучшения». Например, у некоторых производителей модно перенастраивать системный киллер (OOM Killer) приложений на совсем параноидальный режим, в котором приложения в фоне живут совсем недолго. Они показывают чудесные статистики про жизнь устройства от одной зарядки. Чтобы бы получить такие показатели, маркетологи кладут устройство без сторонних приложений на полку и ничего не делают. Так как параноидальный киллер поубивал всё, что можно, телефон живёт действительно днями. А как начинаешь его использовать в реальности, так то антивирус сканирование никогда не можешь завершить, то текст на сайте не прочтёшь, потому что браузер в фоне не может выжить, то на звонок не можешь ответить, потому что звонилке нужно несколько секунд, чтобы подняться из небытия, то нажатие кнопки Home не показывает стартовый экран по пять секунд. Запуск приложений очень долгий, в фоне они сразу погибают, а живёт устройство всего несколько часов активной работы. Знакомая ситуация?

    Ещё хуже, когда это крупный производитель. Если производитель так себе, то Google выдвигает достаточно жёсткие требования к качеству прошивки. Если их не выполнить, то нельзя будет пройти сертификацию прошивки у самого Google. Без этой сертификации Google не разрешает использовать Google Play Services. Без них не будет работать Google Play, покупки и всё такое прочее. Но вот крупных производителей Google побаивается и идёт им на уступки. С другой стороны, есть очень мелкие производители и китайцы. Тем настолько наплевать на Google, потому что Google на них наплевать, что они встраивают что угодно безо всяких сертификаций. И под что угодно я имею в виду и Play Services, и трояны. Это не страшилка от Лаборатории Касперского, это реальная проблема, с которой сталкивались, скажем, пользователи устройств от Highscreen. Пришло очередное обновление прошивки и принесло им китайской рекламы. Потому что производитель устройств Highscreen, на самом деле, не российская фирма, а китайская. Ей всё равно, что там происходит в какой-то России.

    Вот эта открытость создаёт большую проблему. Потому что одно дело натыкаться на проблемы, которые вызваны ошибкой в драйверах и только, ведь производителей аппаратных начинок не так, чтобы очень много, а другое — натыкаться на проблемы, которые вызваны тем, производитель прошивки решил, что он умнее Google, который рулит AOSP. Другими словами, нет проблемы поддержать устройства, есть проблема поддержать конкретные версии конкретных прошивок на конкретных аппаратах в конкретных странах.

    Это звучит страшно, наверное. И хотя разработчики справляются (ну а куда деваться?), но производители всё равно не дают скучають и постоянно выкидывают что-нибудь эдакое в своих прошивках. Я расскажу о реальных ситуациях, которые произошли только за последний год и которые не были обнаружены ни мной, ни моими коллегами, о которых сообщали уже пользователи и для которых пришлось искать обходные решения.

PIN коды Samsung, Meizu и других производителей

    Как я уже рассказывал, имея права Администратора устройства, приложения могут ставить свои пароли. Чем отличается пароль от PIN кода? С точки зрения самого Android разницы нет. И пин-код, и пароль, и рисунок — всё это пароли. Но для каждого из этих вариантов показывается разный интерфейс. Например, для пин-кодов поднимается клавиатура, где доступны только цифры, а для паролей — и цифры, и буквы.
    Это различие определяется так называемой политикой пароля. Перед тем, как установить пароль, приложение с правами Device Admin передаёт системе политику, которая и сообщает, каким должен быть этот пароль. Если сообщается, что нужно и буквы, и цифры, то пользователю будет отображаться интерфейс пароля (то есть клавиатура с полным набором символов). Если в политике будет сказано «только цифры», то пользователь будет видеть интерфейс для ввода пин-кода.

    Таково поведение самого Android. В AOSP графический интерфейс строго следует этому поведению на всех уровнях, от той низкой части Android, которая занимается безопасностью, до той верхней части Android, графической оболочки, которая рисует кнопочки, в которые и тычет пользователь. Но Meizu и некоторые другие китайские производители решили внести свои улучшения. Они решили, что пин-код может иметь длину строго в четыре цифры. Ни больше, ни меньше. Это своё решение они реализовали в самой верхней части — в графической оболочке. При этом нижний уровень Android они не меняли, он сохранил своё поведение. Так вот, с точки зрения базового Android, всё это пароли, длина которых не имеет такой фиксации.
    И что получилось? Многие пользователи для приложений-антиворов задавали пароли длиной пять и более символов. И решили проверить работу антивора, отправив команду блокировки. Антивор честно отработал команду, заблокировал устройство, честно передав пароль системе, которая честно его и поставила. И любые другие она принимать тебе не будет. Политика задавала только цифры, потому что их вводить и удобнее, и быстрее. Итог — прошивки от Meizu и подобных принимали четыре введённые цифры и даже не дожидались ввода пятой и дальше. Сразу после четвёртой, безо всяких нажатий на Enter, они сразу посылали их в Android. И тот, само-собой, не принимал эти коды. Всё, устройство нельзя разблокировать! Так что если у вас на смартфоне используется сильно изменённая оболочка Android, лучше не рискуйте с PIN кодами и используйте строго 4 цифры.

    Теперь про Samsung. Начнём с того, что некоторые устройства из линейки Neo имеют баг в прошивке, который отправляет устройства в вечный цикл перезагрузок при попытке установить пустой пароль. Выше я говорил, что пустые пароли нужны для снятия блокировки. Так вот только эти устройства, из всего многообразия устройств на планете (считаем только те, что имеют Google Play Services), уходят в вечную перезагрузку. И для этой проблемы нет решения, потому что низы не могут (то есть приложения не могут исправить это со своей стороны), а верхи — не хотят (Samsung вообще очень быстро отказывается от поддержки своих не топовых устройств).
    Но это я отвлёкся. Ранее я рассказывал про баг в Android 5.0, когда пустой пароль становился реально паролем. Нужно нажимать Enter. Если бы Samsung просто ничего не трогали, то проблемы бы и не было — разработчики уже давно сделали обходные решения со времён Android L. Но разработчики в Samsung решили, что делать доступной кнопку Enter, если ничего не введено в поле пин-кода — это баг. И просто блокируют её нажатие, если ничего не введено, либо был введено, а затем стёрто. А «что угодно» — это уже не пустой ввод и, понятное дело, не принимается Android.

Имитация поведения старших версий Android

    Быстрее всех на новые версии Android обновляются, как известно, Nexus устройства. Многие производители не поддерживают свои устройства и в этом есть определённый плюс с точки зрения разработки и тестирования. Плюс в том, что имея это устройство, будешь чётко уверен, что и у Васи, и у Джона точно такая же прошивка. С точки зрения безопасности это, конечно, огромный минус, но если смотреть именно с позиции безопасности, то кроме Nexus ничего брать нельзя вообще. Тогда ваши данные будут слиты только АНБ :)

    Но бывают ситуации, когда производитель не желает обновлять прошивку до новой версии Android, а выпускает обновление, которое пытается имитировать фишки новой версии. Абсолютно всегда получается мерзость. Приведу конкретные примеры и снова только за последний год.

    В Android 6 появился режим Doze, я о нём писал в блоге. Это специальный режим, который создан для экономии заряда батареи, если устройство во-первых не двигается, во-вторых не на зарядке, в-третьих с погашенным экраном, в-четвёртых лежит в таком состоянии некоторое время. Он реализован очень толково и его внедрение не повлекло никаких проблем, хотя немного и изменило поведение многих приложений. Но это изменение чётко описано в документации и не приводит к ошибкам. Тот, не очень частый случай, когда всё сделано так, что я аж доволен результатом.

    Некоторые производители решили, что обновлять свои устройства до Android 6 — это слишком не выгодно для них. Потому они выпустили обновления, не идентичные натуральным. К примеру один крупный производитель добавил режим сна, типа Doze. Только в отличие от Doze, приложения, попавшие в список «Оптимизированные», останавливались.
    Поясню. В Android есть разница между «убить» приложение и «остановить» его. Kill, как правило, не эффективен, о чём я писал в первой части лекции. Если приложение имеет модуль приёмника широковещательных сообщений или, скажем, службу, то оно будет поднято. Служба может поднять его прямо сразу, а бродкаст ресивер — при рассылке ожидаемых широковещательных сообщений. А вот если остановить приложение специальной кнопкой Force Stop, то будут остановлены все компоненты приложения и их запуск будет невозможен до тех пор, пока пользователь самолично не запустит приложение (это не касается Администраторов устройств и приложений, имеющих доступ к чтению уведомлений). По причинам безопасности, остановку можно сделать либо вручную, либо в режиме отладки. Одно приложение может убить другое, но не может его остановить. А вот система — может. И это использовал производитель. Он останавливал приложения и запускал их заново исходя из своего видения прекрасного. Очень многие приложения были не готовы к такому поведению и падали. Такое поведение даже не предусмотрено в AOSP!

    Но ещё дальше пошёл один китайский производитель. Они решили, что раз экономить батарею, то по-крупному. И просто вырезал бродкаст «система загрузилась». Автозапуска в классическом понимании в Android нет. Можно подписаться на бродкаст, который система будет рассылать, когда она загрузилась, и тогда приложение будет подниматься при получении этого сообщения. Но не у этого производителя. Такая то экономия батареи получается, если приложения не запускаются, пока пользователь сам этого не сделает! Так что если на вашем устройстве WhatsApp какой-нибудь не запускается после перезагрузки, то вот в этом вся причина.

    Ранее я рассказывал, что Meizu изменил поведение графической оболочки при обработке PIN кодов. В прошлом году или около того было обновление прошивки, которое принесло ещё одно непоправимое улучшение и касалось оно безопасности.
    В Android 5 впервые был реализован так называемый Kill Switch. Причём реализован несколько… странно.
    Kill Switch, если кто не знает, это превращение устройства в кирпич с экраном и батарейкой, если тот, кто держит его в руках, не прошёл «тест на владение», то есть не доказал, что он является реальным владельцем. В чём же странность? Google поддержал Kill Switch в Android 5.0, но работает он в 5.1 и выше. Причём работает ТОЛЬКО если устройство изначально шло с Android 5.0 и выше. Вдумайтесь, Nexus 5, который изначально был на Android 4.3 и имеет сейчас Android 6, не защищён Kill Switch'ем, а Nexus 6, который шёл с предустановленным Android 5.0 — защищён.
    Конкретно в Android подтверждение реализовано достаточно просто. Если устройству был сделан сброс в то время, когда оно было защищено паролем, то в мастере первоначальной настройки нужно ввести логин и пароль учётной записи Google, которая там была перед сбросом. Не знаешь их — можешь выбросить устройство. Прошить его нельзя и бесполезно. Нельзя потому что загрузчик по умолчанию заблокирован, а бесполезно, потому что даже если владелец разблокировал загрузчик, проверка делается на уровне Google Play Services, без которых Google Market получить нельзя.
    Meizu решили, что нужно и нам бы и им такое заиметь. Но так как они из Китая, то там всё плохо с Google Services. Вот и сделали свою реализации. На самом деле сделали её, в целом, правильно, гораздо лучше, чем Google. После сброса, а также при попытке попасть в загрузчик, нужно ввести пароль блокировки, который был до сброса… Вы поняли, да? Там был ровно тот же самый баг с обработкой PIN кодов и его нельзя ввести, если он не равен строго четырём символам. Всё, только замена по гарантии!

    P. S. А изменения в API всё ещё вносят: https://code.google.com/p/android/issues/detail?id=204212 Теперь, в Android N, администраторы устройства могут устанавливать пароли, но не могут их изменить или сбросить, если пароль уже был установлен. Изменение и сброс теперь разрешены только Владельцу устройства (Device Owner) — сущности, которая используется в Android for Works.