Release keys android что это
Ключи, учетные данные и хранилище на Android
Russian (Pусский) translation by Ellen Nelson (you can also view the original English article)
В предыдущем материале о безопасности пользовательских данных Android мы рассмотрели шифрование данных с помощью предоставленного пользователем кода. В этом уроке перейдём к хранению учётных данных и ключей. Я начну с ознакомления с учётными данными и закончу примером защиты данных с помощью хранилища ключей KeyStore.
Часто, при работе со сторонней службой, требуется какая-то форма авторизации. Она может быть настолько простой, как /login на стороне пользователя, которая принимает имя пользователя и пароль.
Сначала может показаться, что простое решение — это собрать UI, который будет предлагать пользователю войти в систему, а затем записать и сохранить их данные для входа. Однако, это не лучший метод, так как нашему приложению не нужно знать данные для входа в сторонний аккаунт. Вместо этого, мы можем использовать Диспетчер учётных записей (Account Manager), который уполномочен отрабатывать эту конфиденциальную информацию для нас.
Диспетчер учётных записей
Диспетчер учётных записей (Account Manager) — это централизованный помощник для работы с учётными данными пользователя, поэтому вашему приложению, иметь дело с паролями напрямую не нужно. Часто он предоставляет токен вместо реального имени пользователя и пароля, который можно использовать для выполнения аутентифицированных запросов к службе. Например, при запросе токена OAuth2.
Иногда, вся необходимая информация уже хранится на устройстве, а иногда Account Manager придётся обращаться к серверу за новым токеном. Вы, наверное, видели раздел Учётные записи в Настройках вашего устройства для разных приложений. И вот как, мы можем получить список доступных учётных записей:
Так как, у каждой службы будет свой способ проверки подлинности и хранения личных учётных данных, Диспетчер учётных записей предоставляет модули проверки подлинности для реализации сторонней службой. Хотя Android может выполнять вход на многие популярные сервисы, для вашего приложения, вы можете написать свой собственный обработчик авторизации учётной записи и хранилища учётных данных. Это позволит убедиться, что учётные данные зашифрованы. Имейте также в виду, что учётные данные в Диспетчере учётных записей, которые используются другими службами, могут храниться в виде открытого текста, что делает их видимыми для любого, кто имеет рут-права на своё устройство.
Временами вам нужно будет иметь дело с ключами или сертификатами для отдельного лица или сущности, вместо просто данных для входа. Например, когда стороннее приложение отправляет вам файл сертификата, который вам нужно сохранить. Самый распространённый сценарий — это когда приложению нужно авторизироваться на сервере частной организации.
В следующем уроке, мы рассмотрим использование сертификатов для аутентификации и безопасной связи, ну а пока, я всё же хочу рассмотреть, как хранить эти элементы. Изначально Keychain API был создан для очень конкретного использования — установка закрытого ключа или пары сертификатов из файла PKCS#12.
Keychain — связка ключей
Представленный в Android 4.0 (API Level 14), Keychain API управлял ключами. В частности, это работает с объектами PrivateKey и X509Certificate и обеспечивает более безопасный контейнер, чем использование хранилища данных вашего приложения. Связано это с тем, что разрешения закрытых ключей открывают доступ к ключам только вашему приложению и только после авторизации пользователя. Это означает, что, прежде чем, вы сможете использовать хранилище учётных данных, на устройстве должен быть настроен экран блокировки. Кроме того, объекты в связке ключей можно объединить с защитой от оборудования, если доступно.
Код установки сертификата выглядит следующим образом:
Пользователю будет предложено ввести пароль для доступа к закрытому ключу и указать имя сертификата. Для получения ключа, в следующем коде представлен пользовательский интерфейс, который позволяет пользователю выбирать ключ из списка установленных ключей.
Вооружившись этими знаниями, теперь давайте посмотрим, как мы можем использовать хранилище учётных данных для сохранения конфиденциальных данных.
KeyStore
В предыдущем уроке, мы рассмотрели защиту данных с помощью предоставляемого пользователем пароля. Такой вариант хорош, но требования к приложениям часто уводят от того, чтобы пользователи каждый раз входили в систему и запоминали дополнительный пароль.
Вот где можно использовать KeyStore API. Начиная с API 1, KeyStore используется системой для хранения учётных данных WiFi и VPN. Начиная с 4.3 (API 18), вы можете работать с асимметричными ключами конкретного приложения, а в Android M (API 23) можно хранить симметричный ключ AES. Таким образом, хотя API не позволяет хранить конфиденциальные строки напрямую, эти ключи можно сохранить, а затем использовать для шифрования строк.
Преимущество хранения ключа в хранилище ключей заключается в том, что он позволяет работать с ключами без раскрытия секретного содержимого этого ключа; данным ключа не место в приложении. Помните, что ключи защищаются разрешениями, так что только ваше приложение может получить к ним доступ, и они могут быть дополнительно защищены аппаратным обеспечением, если устройство поддерживает это. Создаётся контейнер, который усложняет извлечение ключей с устройства.
Генерирование нового случайного ключа
Поскольку хранение незащищённого ключа вместе с зашифрованными данными, это как прятать ключ от дома под половик, эти параметры пытаются защитить ключ в состоянии покоя в случае взлома устройства. Примером может служить автономный дамп данных устройства. Если пароль устройства не известен, эти данные, по сути, бесполезны.
Шифрование данных
Расшифровка в массив байтов
Смотрим на примере
Теперь мы можем посмотреть на пример!
Использование асимметричных ключей RSA для старых устройств
Это хорошее решение для хранения данных в версии M и выше, но что, если ваше приложение поддерживает более ранние версии? Хотя симметричные ключи AES не поддерживаются в M, поддерживаются асимметричные ключи RSA. Это означает, что для достижения того же результата, мы можем использовать RSA ключи и шифрование.
Чтобы получить ключ, сделайте вот так:
Довольно много кода! Для простоты примеров, я пропустил обработку исключений. Но помните, что для итогового кода не рекомендуется просто перехватывать все случаи Throwable в одном операторе catch.
Заключение
На этом урок по работе с учётными данными и ключами завершён. Большая часть неразберихи вокруг ключей и хранилища связана с эволюцией ОС Android, но вы можете выбрать, какое решение использовать, учитывая уровень API, поддерживаемый вашим приложением.
Теперь, когда мы рассмотрели лучшие примеры защиты данных в состоянии покоя, следующий урок будет сосредоточен на защите данных при передаче.
Боремся со Status 7. Как работает механизм OTA-обновлений и почему он дает сбои
Довольно часто юзеры, привыкшие рутовать прошивки, устанавливать разного рода системный софт, менять ядра и по-другому издеваться над прошивкой, обнаруживают, что установить OTA-обновление невозможно. Оно просто не встает, ругаясь на измененные системные файлы, неправильные цифровые ключи и всякое прочее. В этой статье я расскажу о самой механике обновления, причинах возникновения проблем и о том, как их решить.
Как это работает
Первыми новые версии Android традиционно получают последние из устройств Nexus. Когда новая версия прошивки готова для широкой публики, полный образ размещается по адресу developers.google.com/android/nexus/images. Вскоре после этого начинается распространение прошивки по воздуху. Как рассказывает один из разработчиков Google Дэн Моррилл (Dan Morrill), сначала ОТА рассылается на 1% устройств. Это происходит рандомно, независимо от региона или места покупки телефона/планшета. В это время отлавливаются баги, что позволяет приостановить обновление при наличии критических ошибок у большого числа пользователей.
Далее в течение пары недель обновление распространяется для 25, 50, 100% пользователей. То есть на первом этапе шанс на получение обновления имеет одно устройство из ста. Если обновление не получено, то устройство выпадает из списка и повторное неоднократное нажатие на кнопку «Проверить наличие обновлений» автоматически переносит устройство в конец списка. Когда запускается новый этап рассылки, нажатие на кнопку дает следующий шанс получить обновление уже 25%. Так как устройство само проверяет наличие обновления раз в сутки (или при перезагрузке), то нажатие на кнопку может «выстрелить» раньше, чем это случилось бы само по себе. Но опять-таки проверка будет только один раз. Дальнейшие нажатия не помогут. Это не та ситуация, когда «кто первый нажал, тот первый получил». В любом случае обновление по воздуху придет всем в течение пары недель. Самые нетерпеливые могут прошить обновление руками (об этом ниже).
Уведомление о наличии обновления
Форсируем обновление
Ускорить получение обновления можно двумя способами. Первый — очистка данных Google Services Framework с последующей перезагрузкой устройства. Крайне не рекомендуемый способ, который осуждают даже инженеры Гугла. Этот способ вызывает множество негативных эффектов, главный из которых — смена идентификатора для GCM (Google Cloud Messenger). Этот идентификатор нужен во всех программах Гугла и множестве других приложений, использующих функции push-уведомлений. И если в некоторых программах побороть эффекты относительно легко, то для многих других последствия могут быть более печальны. Все приложения просто перестанут принимать push-уведомления, основанные на GCM, пока не получат новый идентификатор. Некоторые приложения делают проверку часто, некоторые редко. Для части поможет очистка данных приложения. А те приложения, которые используют GCM ID в качестве идентификатора на своих серверах, могут иметь более глубокие проблемы.
Стоковый recovery
Второй — установка обновления руками через консоль восстановления. Вскоре после запуска ОТА в профильных темах устройств на ресурсах 4PDA и XDA появляются файлы вида хеш.signed-hammerhead-LRX21O-from-KTU84P.c1a33561.zip, в названии которых содержится хеш файла, марка устройства, а также версии прошивок для обновления (на какую, с какой). На компе необходимо иметь папку с утилитами ADB и fastboot. Я использую последние версии из Android SDK. В ту же папку нужно положить скачанный архив с ОТА-обновлением. Также необходимо иметь правильно установленные драйверы для устройства, которые могут конфликтовать с ранее установленными драйверами для других устройств.
Само устройство следует перевести в режим восстановления (recovery). Для этого на выключенном устройстве зажимаем одновременно кнопки
и попадаем в загрузчик, кнопкой громкости выбираем Recovery mode, входим в него кнопкой Power. Появится лежачий Android с восклицательным знаком. Это не ошибка, пугаться не стоит. Необходимо на этом экране коротко нажать
, после чего и загрузится стоковый рекавери. В нем необходимо выбрать кнопками громкости пункт apply update from ADB и подтвердить кнопкой включения. Далее необходимо подключить телефон/планшет к компу. Запускаем консоль, переходим в папку с ADB и архивом обновления и вводим следующую команду (для файла, приведенного выше):
После этого на телефон установится ОТА и он перезагрузится.
Блок-врезка: Как скачать обновление через сотовую сеть
Уведомление о доступности ОТА может прийти, когда устройство не подключено к Wi-Fi. При этом появится пометка, что файл доступен для скачивания по Wi-Fi до определенной даты (около недели), а сама кнопка «Скачать» будет неактивна. Это сделано для экономии денег юзера. Если подключение к Wi-Fi в ближайшее время не предвидится, то можно обмануть телефон и скачать обновление через 3G/4G, просто переведя дату в телефоне вперед, позже даты, указанной в уведомлении, и перегрузив устройство.
Под стоковой (stock — из магазина) прошивкой понимается наличие заводского ядра, recovery, отсутствие модификаций, полученных в том числе с помощью root.
Модифицированная прошивка
Если у тебя разблокирован загрузчик, стоит кастомный recovery, получен root, который активно используют различные программы, и применены различные модификации, то с вероятностью 99% обновление не установится. Даже при возврате стокового recovery при прошивке через ADB будет выдавать ошибку Status 7. Кастомный recovery также будет писать ошибку, ругаясь на измененные файлы. Побороть эту проблему можно, вернув смартфон к заводской прошивке, но это не наш метод. Мы разберемся с ней, расковыряв файл обновления, выясним, на каком месте спотыкается установка, и устраним проблему. И все это на примере самого крупного обновления Nexus 5 — с версии 4.4.4 (KTU84P) на 5.0 (LRX21O).
Механика работы ОТА
Итак, обновление с 4.4.4 на 5.0 стало самым крупным за последнее время с весом архива в 491 Мб. В связи со сменой Dalvik на ART практически весь код был модифицирован. Так что же содержит архив? Как видно на скриншоте «Файлы из архива с обновлением до 5.0», внутри архива находятся образы бутлоадера (различные разделы), каталоги META-INF, patch и system.
Файлы из архива с обновлением до 5.0
Для минимизации количества трафика и уменьшения нагрузки на серверы, а также для снижения затрат конечного пользователя структура обновления построена так, что файлы с большим количеством изменений или написанные с нуля находятся в каталоге system и меняются целиком. А файлы с небольшими по меркам Гугла изменениями не заменяются, а патчатся, то есть изменяются куски кода внутри файла. Эти файлы находятся внутри каталога patch и имеют расширение.р. Это хорошо видно, если сравнить файлы в /system/bin и /patch/system/bin. При этом для создания патча используется хорошо знакомый юниксоидам bsdiff, позволяющий из двух бинарников получить дельту (файл с разницей между файлами).
Само же волшебство происходит по воле updater-script, который находится в /META-INF/com/google/android. Именно его мы и рассмотрим подробнее. Сам файл весит 463 Кб и содержит строки кода, отвечающие за процесс применения ОТА-обновления (на самом деле это скриптовый язык Edify, интерпретатор которого находится в том же каталоге и носит имя update-binary. — Прим. ред.). Вот что он содержит в нашем случае. Сначала монтируется раздел /system (достаточно стандартная для Linux строка монтирования, схожая с теми, что находятся в /etc/fstab):
Далее скрипт проверяет модель устройства и версию прошивки с помощью чтения системной переменной ro.build.fingerprint (обрати внимание, что он не берет ее из файла /system/build.prop, а запрашивает у самого recovery, поэтому обновления нельзя поставить с помощью кастомной консоли восстановления, хотя до 5.0 это было возможно). Здесь и далее троеточие это сокращенные строки:
Как видно выше, на «неродное» устройство обновление не встанет, зато его можно повторно накатить на версию 5.0. Также скрипт проверяет, подписана ли прошивка официальными ключами Google (release-keys). Из-за этого у многих пользователей возникают проблемы. Далее начинается проверка наличия и целостности отдельных файлов с помощью сверки хешей SHA-1. Для этого используются две функции: sha1_check(), принимающая в качестве аргументов имя файла и хеш, и apply_patch_check(), принимающая три аргумента: имя файла, и два хеша. Первая используется просто для проверки целостности файла, вторая проверяет, не был ли файл уже пропатчен. Для простоты длинные хеши в коде ниже заменены на многоточие:
Для примера показаны только две проверки. По факту проверяются все файлы, которые подлежат замене или изменению патчем. В коде видно, что обновление выдаст ошибку, если, например, был изменен или удален файл /system/app/Drive.apk. В конце блока проверки скрипт проверяет ядро, доступное место в /system и радио:
То есть данное обновление не встанет, если стоит кастомное ядро или модификация радио. Следующим шагом идет удаление старых файлов с устройства перед их заменой на новые и удаление файлов, которые не нужны на новой прошивке:
Далее патчатся все необходимые файлы с предварительной проверкой хеша SHA-1. Патчинг выполняется с помощью функции apply_patch(), которая принимает имена файлов для патчинга и несколько хешей: хеш оригинала, хеш патча и хеш результата. Последним аргументом идет имя файла с патчем. Как и раньше, все хеши в коде ниже сокращены до многоточия:
Последним патчится ядро и RAM-диск:
Следующий блок переносит на устройство файлы, которые не попадают под патч и должны быть заменены целиком. Часть из них затем перемещается:
Удаляются ненужные файлы, расставляются симлинки, права доступа и флаги (здесь на многоточие заменены именно права доступа и флаги):
Прошиваются бутлоадер и сопутствующие разделы:
Последним меняется build.prop, в который записывается в том числе новая версия прошивки. Сделано это для того, чтобы при возникновении ошибки на самом последнем этапе, когда почти все файлы уже перенесены, прервать обновление и сохранить номер текущей версии прошивки в файле на устройстве. Тогда при нажатии кнопки «Проверить обновление» можно запустить его снова.
В конце скрипта раздел /system перемонтируется, и начинается проверка правильности применения обновления, сверяется SHA-1 хеш новых файлов и /system размонтируется:
После чего устройство перегружается в новую систему.
Updater-script как он есть
Кастомный recovery
До недавнего времени прошить архив ОТА-обновления в большинстве случаев (если не было проверки recovery для его замены) можно было из кастомного recovery, просто закинув файл на устройство и выбрав install zip. Но начиная со скрипта для обновления 5.0 скрипт поменялся. Предыдущие версии проверяли файл /system/build.prop:
Текущие скрипты проверяют не файл, а значение системной переменной напрямую, запрашивая его у recovery:
А если разобрать кастомный recovery (для примера TWRP версии 2.8.0.0), то можно увидеть следующие строки:
Версия TWRP 2.8.6.1 имеет в коде следующие строки (обрати внимание на слово omni во второй строке, разработчик TWRP с ником Dees Troy — еще и один из активных разработчиков OmniROM):
А последние версии CWM Touch и Philz подписаны так:
Именно эти значения и возвращает при проверке скрипт, прерывая обновление в самом начале и выдавая ошибку о несоответствии версии Android на устройстве.
Вот какой ответ ты получишь при попытке установить обновление 5.0.2 на Nexus 7 из кастомного recovery
Обновление 4.4.3–4.4.4
Для сравнения можно привести предыдущее обновление с версии KTU84M на KTU84P. Обновление мелкое и весит всего 2,5 Мб. В основном касается улучшений безопасности. Если открыть архив, то можно увидеть, что патчится только небольшое количество системных файлов и радио, соответственно, скрипт и проверяет только их. Это обновление нормально устанавливалось с рутом, кастомным ядром и работающим Xposed Framework, так как на наличие изменений все это не проверяется.
Обновление для Nexus 6 и Nexus 9
У последних устройств от Google структура скрипта в корне другая. Для этих и (судя по всему) последующих устройств Nexus Google добавила в сборочный скрипт, формирующий ОТА-обновление, функцию генерации поблочного обновления. Такое обновление сверяет и обновляет не отдельные файлы, а блоки в файловой системе /system. Далее в примере «66. 524256» — это длинные списки адресов блоков:
Это позволило инженерам Google существенно упростить и ускорить применение ОТА-обновления для конечных устройств, а сам updater-script теперь занимает всего 5 Кб. Но это обернулось головной болью для продвинутых пользователей. Ведь теперь любые изменения в системном разделе вызовут сбой. Включая наличие лишних файлов. Даже факт монтирования системы как R/W приведет к изменению хеша суперблока ФС.
Заключение
Подводя итоги статьи, можно сделать следующие выводы:
Пара слов от редактора
До недавнего времени OTA-обновления в каcтомных прошивках (CyanogenMod, Paranoid) всегда приходили в виде zip’а с полной версией прошивки и было абсолютно неважно, какие изменения вносились в систему до этого. Прошивка всегда устанавливалась заново (с сохранением данных юзера и gapps, естественно), однако в CyanogenMod 11 появилась функция инкрементальных обновлений, но гораздо более простая в сравнении с той, что используется Google. Обновление просто проверяет целостность прошивки и заменяет те файлы, которые изменились с прошлой версии (обычно ночной сборки), без всяких патчей. Причем, если ты пропустишь одно из обновлений, следующее по старинке придет в виде полного обновления. Просто и удобно.
Более интересный метод используется в OmniROM. Для обновления она использует бинарные патчи, но совсем не так, как это делает Google. Первое OTA-обновление всегда скачивается полностью, после чего сохраняется на карте памяти, прошивается, но не удаляется с карты. Следующее OTA-обновление уже приходит в виде единого бинарного патча, после чего патч накладывается на сохраненное в прошлый раз на карте памяти обновление и уже оно прошивается. Изюминка этого метода в том, что патч накладывается не на систему, а на файл с прошлым обновлением и смартфон каждый раз прошивается как бы с нуля (но с сохранением данных и настроек). Почти идеальный метод — трафик экономится, а беспокоиться о конфликтах с измененной системой не надо.
Экран установки обновлений в CyanogenMod 12
Впервые опубликовано в журнале Хакер #196.
Автор: Дмитрий «BRADA» Подкопаев
Как работают OTA-обновления в Android, и почему они дают сбои
Содержание статьи
Довольно часто юзеры, привыкшие рутовать прошивки, устанавливать разного рода системный софт, менять ядра и по-другому издеваться над прошивкой, обнаруживают, что установить OTA-обновление невозможно. Оно просто не встает, ругаясь на измененные системные файлы, неправильные цифровые ключи и всякое прочее. В этой статье я расскажу о самой механике обновления, причинах возникновения проблем и о том, как их решить.
Как это работает
Первыми новые версии Android традиционно получают последние из устройств Nexus. Когда новая версия прошивки готова для широкой публики, полный образ размещается по адресу developers.google.com/android/nexus/images. Вскоре после этого начинается распространение прошивки по воздуху. Как рассказывает один из разработчиков Google Дэн Моррилл (Dan Morrill), сначала ОТА рассылается на 1% устройств. Это происходит рандомно, независимо от региона или места покупки телефона/планшета. В это время отлавливаются баги, что позволяет приостановить обновление при наличии критических ошибок у большого числа пользователей.
Далее в течение пары недель обновление распространяется для 25, 50, 100% пользователей. То есть на первом этапе шанс на получение обновления имеет одно устройство из ста. Если обновление не получено, то устройство выпадает из списка и повторное неоднократное нажатие на кнопку «Проверить наличие обновлений» автоматически переносит устройство в конец списка. Когда запускается новый этап рассылки, нажатие на кнопку дает следующий шанс получить обновление уже 25%. Так как устройство само проверяет наличие обновления раз в сутки (или при перезагрузке), то нажатие на кнопку может «выстрелить» раньше, чем это случилось бы само по себе. Но опять-таки проверка будет только один раз. Дальнейшие нажатия не помогут. Это не та ситуация, когда «кто первый нажал, тот первый получил». В любом случае обновление по воздуху придет всем в течение пары недель. Самые нетерпеливые могут прошить обновление руками (об этом ниже).
Уведомление о наличии обновления
Хакер #196. Все о Docker
Форсируем обновление
Ускорить получение обновления можно двумя способами. Первый — очистка данных Google Services Framework с последующей перезагрузкой устройства. Крайне не рекомендуемый способ, который осуждают даже инженеры Гугла. Этот способ вызывает множество негативных эффектов, главный из которых — смена идентификатора для GCM (Google Cloud Messenger). Этот идентификатор нужен во всех программах Гугла и множестве других приложений, использующих функции push-уведомлений. И если в некоторых программах побороть эффекты относительно легко, то для многих других последствия могут быть более печальны. Все приложения просто перестанут принимать push-уведомления, основанные на GCM, пока не получат новый идентификатор. Некоторые приложения делают проверку часто, некоторые редко. Для части поможет очистка данных приложения. А те приложения, которые используют GCM ID в качестве идентификатора на своих серверах, могут иметь более глубокие проблемы.
Стоковый recovery
Второй — установка обновления руками через консоль восстановления. Вскоре после запуска ОТА в профильных темах устройств на ресурсах 4PDA и XDA появляются файлы вида хеш.signed-hammerhead-LRX21O-from-KTU84P.c1a33561.zip, в названии которых содержится хеш файла, марка устройства, а также версии прошивок для обновления (на какую, с какой). На компе необходимо иметь папку с утилитами ADB и fastboot. Я использую последние версии из Android SDK. В ту же папку нужно положить скачанный архив с ОТА-обновлением. Также необходимо иметь правильно установленные драйверы для устройства, которые могут конфликтовать с ранее установленными драйверами для других устройств.
После этого на телефон установится ОТА и он перезагрузится.
Как скачать обновление через сотовую сеть
Уведомление о доступности ОТА может прийти, когда устройство не подключено к Wi-Fi. При этом появится пометка, что файл доступен для скачивания по Wi-Fi до определенной даты (около недели), а сама кнопка «Скачать» будет неактивна. Это сделано для экономии денег юзера. Если подключение к Wi-Fi в ближайшее время не предвидится, то можно обмануть телефон и скачать обновление через 3G/4G, просто переведя дату в телефоне вперед, позже даты, указанной в уведомлении, и перегрузив устройство.
Модифицированная прошивка
Если у тебя разблокирован загрузчик, стоит кастомный recovery, получен root, который активно используют различные программы, и применены различные модификации, то с вероятностью 99% обновление не установится. Даже при возврате стокового recovery при прошивке через ADB будет выдавать ошибку Status 7. Кастомный recovery также будет писать ошибку, ругаясь на измененные файлы. Побороть эту проблему можно, вернув смартфон к заводской прошивке, но это не наш метод. Мы разберемся с ней, расковыряв файл обновления, выясним, на каком месте спотыкается установка, и устраним проблему. И все это на примере самого крупного обновления Nexus 5 — с версии 4.4.4 (KTU84P) на 5.0 (LRX21O).
Под стоковой (stock — из магазина) прошивкой понимается наличие заводского ядра, recovery, отсутствие модификаций, полученных в том числе с помощью root.
Механика работы ОТА
Итак, обновление с 4.4.4 на 5.0 стало самым крупным за последнее время с весом архива в 491 Мб. В связи со сменой Dalvik на ART практически весь код был модифицирован. Так что же содержит архив? Как видно на скриншоте «Файлы из архива с обновлением до 5.0», внутри архива находятся образы бутлоадера (различные разделы), каталоги META-INF, patch и system.
Файлы из архива с обновлением до 5.0
Само же волшебство происходит по воле updater-script, который находится в /META-INF/com/google/android. Именно его мы и рассмотрим подробнее. Сам файл весит 463 Кб и содержит строки кода, отвечающие за процесс применения ОТА-обновления (на самом деле это скриптовый язык Edify, интерпретатор которого находится в том же каталоге и носит имя update-binary. — Прим. ред.). Вот что он содержит в нашем случае. Сначала монтируется раздел /system (достаточно стандартная для Linux строка монтирования, схожая с теми, что находятся в /etc/fstab):
Далее скрипт проверяет модель устройства и версию прошивки с помощью чтения системной переменной ro.build.fingerprint (обрати внимание, что он не берет ее из файла /system/build.prop, а запрашивает у самого recovery, поэтому обновления нельзя поставить с помощью кастомной консоли восстановления, хотя до 5.0 это было возможно). Здесь и далее троеточие это сокращенные строки:
Как видно выше, на «неродное» устройство обновление не встанет, зато его можно повторно накатить на версию 5.0. Также скрипт проверяет, подписана ли прошивка официальными ключами Google (release-keys). Из-за этого у многих пользователей возникают проблемы. Далее начинается проверка наличия и целостности отдельных файлов с помощью сверки хешей SHA-1. Для этого используются две функции: sha1_check(), принимающая в качестве аргументов имя файла и хеш, и apply_patch_check(), принимающая три аргумента: имя файла, и два хеша. Первая используется просто для проверки целостности файла, вторая проверяет, не был ли файл уже пропатчен. Для простоты длинные хеши в коде ниже заменены на многоточие:
Для примера показаны только две проверки. По факту проверяются все файлы, которые подлежат замене или изменению патчем. В коде видно, что обновление выдаст ошибку, если, например, был изменен или удален файл /system/app/Drive.apk. В конце блока проверки скрипт проверяет ядро, доступное место в /system и радио:
То есть данное обновление не встанет, если стоит кастомное ядро или модификация радио. Следующим шагом идет удаление старых файлов с устройства перед их заменой на новые и удаление файлов, которые не нужны на новой прошивке:
Далее патчатся все необходимые файлы с предварительной проверкой хеша SHA-1. Патчинг выполняется с помощью функции apply_patch(), которая принимает имена файлов для патчинга и несколько хешей: хеш оригинала, хеш патча и хеш результата. Последним аргументом идет имя файла с патчем. Как и раньше, все хеши в коде ниже сокращены до многоточия:
Последним патчится ядро и RAM-диск:
Следующий блок переносит на устройство файлы, которые не попадают под патч и должны быть заменены целиком. Часть из них затем перемещается:
Удаляются ненужные файлы, расставляются симлинки, права доступа и флаги (здесь на многоточие заменены именно права доступа и флаги):
Прошиваются бутлоадер и сопутствующие разделы:
Последним меняется build.prop, в который записывается в том числе новая версия прошивки. Сделано это для того, чтобы при возникновении ошибки на самом последнем этапе, когда почти все файлы уже перенесены, прервать обновление и сохранить номер текущей версии прошивки в файле на устройстве. Тогда при нажатии кнопки «Проверить обновление» можно запустить его снова.
В конце скрипта раздел /system перемонтируется, и начинается проверка правильности применения обновления, сверяется SHA-1 хеш новых файлов и /system размонтируется:
После чего устройство перегружается в новую систему.
Updater-script как он есть
Кастомный recovery
До недавнего времени прошить архив ОТА-обновления в большинстве случаев (если не было проверки recovery для его замены) можно было из кастомного recovery, просто закинув файл на устройство и выбрав install zip. Но начиная со скрипта для обновления 5.0 скрипт поменялся. Предыдущие версии проверяли файл /system/build.prop:
Текущие скрипты проверяют не файл, а значение системной переменной напрямую, запрашивая его у recovery:
А если разобрать кастомный recovery (для примера TWRP версии 2.8.0.0), то можно увидеть следующие строки:
Версия TWRP 2.8.6.1 имеет в коде следующие строки (обрати внимание на слово omni во второй строке, разработчик TWRP с ником Dees Troy — еще и один из активных разработчиков OmniROM):
А последние версии CWM Touch и Philz подписаны так:
Именно эти значения и возвращает при проверке скрипт, прерывая обновление в самом начале и выдавая ошибку о несоответствии версии Android на устройстве.
Вот какой ответ ты получишь при попытке установить обновление 5.0.2 на Nexus 7 из кастомного recovery
Обновление 4.4.3–4.4.4
Для сравнения можно привести предыдущее обновление с версии KTU84M на KTU84P. Обновление мелкое и весит всего 2,5 Мб. В основном касается улучшений безопасности. Если открыть архив, то можно увидеть, что патчится только небольшое количество системных файлов и радио, соответственно, скрипт и проверяет только их. Это обновление нормально устанавливалось с рутом, кастомным ядром и работающим Xposed Framework, так как на наличие изменений все это не проверяется.
Обновление для Nexus 6 и Nexus 9
У последних устройств от Google структура скрипта в корне другая. Для этих и (судя по всему) последующих устройств Nexus Google добавила в сборочный скрипт, формирующий ОТА-обновление, функцию генерации поблочного обновления. Такое обновление сверяет и обновляет не отдельные файлы, а блоки в файловой системе /system. Далее в примере «66. 524256» — это длинные списки адресов блоков:
Это позволило инженерам Google существенно упростить и ускорить применение ОТА-обновления для конечных устройств, а сам updater-script теперь занимает всего 5 Кб. Но это обернулось головной болью для продвинутых пользователей. Ведь теперь любые изменения в системном разделе вызовут сбой. Включая наличие лишних файлов. Даже факт монтирования системы как R/W приведет к изменению хеша суперблока ФС.
Заключение
Подводя итоги статьи, можно сделать следующие выводы:
Пара слов от редактора
До недавнего времени OTA-обновления в каcтомных прошивках (CyanogenMod, Paranoid) всегда приходили в виде zip’а с полной версией прошивки и было абсолютно неважно, какие изменения вносились в систему до этого. Прошивка всегда устанавливалась заново (с сохранением данных юзера и gapps, естественно), однако в CyanogenMod 11 появилась функция инкрементальных обновлений, но гораздо более простая в сравнении с той, что используется Google. Обновление просто проверяет целостность прошивки и заменяет те файлы, которые изменились с прошлой версии (обычно ночной сборки), без всяких патчей. Причем, если ты пропустишь одно из обновлений, следующее по старинке придет в виде полного обновления. Просто и удобно.
Более интересный метод используется в OmniROM. Для обновления она использует бинарные патчи, но совсем не так, как это делает Google. Первое OTA-обновление всегда скачивается полностью, после чего сохраняется на карте памяти, прошивается, но не удаляется с карты. Следующее OTA-обновление уже приходит в виде единого бинарного патча, после чего патч накладывается на сохраненное в прошлый раз на карте памяти обновление и уже оно прошивается. Изюминка этого метода в том, что патч накладывается не на систему, а на файл с прошлым обновлением и смартфон каждый раз прошивается как бы с нуля (но с сохранением данных и настроек). Почти идеальный метод — трафик экономится, а беспокоиться о конфликтах с измененной системой не надо.