Во все времена существования мобильных телефонов, дешевый сегмент всегда оставался одним из самых прибыльных и массовых направлений мобильного бизнеса. Помимо «простозвонилок» в духе Nokia 1100 или 1202, на рынке есть огромный спрос к ультрабюджетным смартфонам ценой до 10 тысяч рублей. И если с дешевыми девайсами у локальных ритейлеров всё понятно (чипсеты 5+ летней давности, минимальный объём ОЗУ и ПЗУ, простенькие TN-матрицы низкого разрешения), то китайские магазины готовы предложить по этой цене устройства с небывало щедрыми характеристиками — и 8Gb RAM, и 512Gb ROM, и Snapdragon 888+. Для большинства покупателей неочевидно, что эти характеристики — обман, а девайс будет обладать лишь базовыми параметрами. Но что самое обидное, производитель закладывает программный и аппаратный срок службы в такие устройства, из-за чего более половины смартфонов выходят из строя через 4-5 месяцев после покупки, а в СЦ за ремонт просят половину цены от нового. Около года назад я купил почти новую китайскую реплику Huawei P30 Pro с родной коробкой и заводской плёнкой всего за 400 рублей, которая уже была не рабочей. Сегодня мы с вами: узнаем о том, как производители экономят и закладывают срок службы в бюджетные мобилки, на практике отремонтируем подобный девайс своими руками (причём ремонт обойдется нам в 70 рублей) и посмотрим, можно ли пользоваться таким бюджетником за 470 рублей на в 2023 на практике. Интересно? Тогда добро пожаловать под кат!
❯ Кто виноват и что нам делать?
Пожалуй, если сейчас зайти на любой маркетплейс и поискать бюджетный Android-смартфон, то мы увидим сотни приложений по вкусным ценам с какими-то небывалыми характеристиками для подобного ценового сегмента. Производители обещают топовые чипсеты Snapdragon, 8Гб DDR4 ОЗУ, 512Гб UFS ПЗУ и качественные IPS-дисплеи. А что самое интересное, эти смартфоны зачастую являются репликами крутых флагманских устройств и выглядят практически точь в точь как оригинальные девайсы.
Таким образом, производитель создаёт дополнительный психологический фактор к покупке такого бюджетного смартфона: «ведь он выглядит как флагман, характеристики флагманские, а чего б и не взять?». Принято считать, что эпоха реплик айфонов и самсунгов прошла лет 10 назад и никто не пытается выделиться за счёт дорогого флагмана, однако рынок точных копий всё ещё живёт и китайские производители продолжают выпускать реплики устройств Apple в огромных объёмах.
Заказав и получив новенький смартфон, возможны несколько реакций от новоиспеченного владельца, обычно в зависимости от его потребностей в мобильном девайсе. Если покупатель действительно ожидает флагманских характеристик от устройства, то тут его ждёт полное разочарование: на самом деле девайс имеет ультрабюджетные характеристики и работает на железе, в лучшем случае, семилетней давности. Нередки случаи выхода новых устройств на базе чипсета MediaTek MT6582 2013 года выпуска — более одиннадцатилетней давности! Огорчение ждёт пользователя и по части ОЗУ с постоянной памятью: зачастую производители ставят только 1Гб DDR2 памяти вместо обещанных 8Гб DDR4 и 8Гб постоянной памяти вместо 512Гб. В качестве ОС, вместо обещанной последней версии Android, пользователя обычно ждёт немного «перекрашенный» под фирменный интерфейс Samsung/Huawei/Apple Android 6 или 8 версии. После этого девайс обычно убирается в дальний ящик «как запасной», отдаётся ребенку или быстренько продается на барахолке за чуть меньшую цену, чем было куплено. При этом «8Gb RAM, 512Gb ROM» — это как бы часть названия устройства, а не маркетинг, так что даже к условному Авито за это не докопаться.
Если же покупателю нужен смартфон с базовым функционалом — т. е. мессенджеры типа Telegram/WhatsApp/Viber, соц. сети а-ля Facebook или VK, а также музыка и какой-то базовый серфинг в браузере, то он может и искреннее обрадоваться своей покупке, даже не обращая внимания на какие-то небольшие подлагивания интерфейса во время работы. Плюсы, помимо отличного (скопированного) дизайна, у таких девайсов тоже есть:
Дисплей: несмотря на то, что дисплеи в таких девайсах довольно низкого разрешения — обычно 540x960 (и это ограничение старого чипсета, а не экономия китайцев), зачастую в чуть более дорогие реплики устанавливают весьма неплохие IPS-матрицы на которые вполне приятно смотреть. В самых бюджетных моделях до сих пор ставят TN, но достаточно качественный, чтобы глаза «не вытекали» от углов обзора.
Звук и микрофон: обычно в подобных девайсах стоит моно-динамик, отличающийся довольно большой громкостью, но немного хромающий в плане качества звучания. Тем не менее, для слабослышащих людей это будет плюсом. В качестве микрофона в таких устройствах до сих пор устанавливаются аналоговые простые микрофоны, которые легко найти и заменить в случае неисправности, ни о каких цифровых микрофонах речи обычно не идет.
Аккумулятор: это плюсом можно назвать лишь 50/50 и зависит от модели к модели. Например, почти все китайские реплики айфонов обладают очень слабыми аккумуляторами (из-за ограничений по размерам корпуса), зачастую ёмкостью менее 2000мАч! Но в некоторые модели (например, клоны Huawei) устанавливают АКБ с честной ёмкостью ~3.000-4.000мАч, которые вполне неплохо держат, учитывая низкое потребление старых бюджетных чипсетов. Ещё один важный момент — в некоторых подобных смартфонах намеренно стоит упрощённая BMS (защитная плата аккумулятора), которая даёт уйти АКБ в глубокий разряд (т. е. ниже 2.8В), что пагубно сказывается на ёмкости аккумулятора, если девайс долго лежит разряженным.
И казалось бы, если взвесить на чаше весов плюсы и минусы девайса, то получается вполне неплохой ультрабюджетный смартфон: как раз купить маме/бабушке/ребенку или себе для базовых задач и пользоваться, не зная проблем! Но есть в бочке мёда две больших ложки дёгтя, связанные с тотальной экономией, которая выливается в поломку устройства спустя несколько месяцев после активного использования. «Бонусом» для производителя — эта ложка дёгтя закладывает самый настоящий «срок службы» девайса.
Первая ложка дёгтя не особо критичная и связана исключительно с программными приколами. Фактически, 8Гб постоянной памяти — это минимум, необходимый для работы относительно свежих версий Android и современных приложений. Поэтому, установив банковский клиент, WhatsApp, Telegram с кучей каналов, которые флудят картинками и VK, пользователь быстро обнаруживает, что памяти осталось очень мало и система предлагает удалить некоторые приложения для её освобождения.
Если проигнорировать это предупреждение и позволить приложениям дальше засорять память, Android со временем вывалится в софтлок, показывая диалог освобождения места при попытке запуска любого приложения — даже настроек! Освободив память, смартфон всё равно не получится сбросить даже через настройки, только через рекавери, что выливается в софтлок — из-за чего неопытные пользователи уже думают, что девайс «сломался» и несут его в сервис/идут покупать новый.
Происходит это из-за того, что китайцы пытаются обмануть Android, подменяя везде объём памяти с реальных 8Гб на 256/512/1024Гб. Android по каким-то причинам не руководствуется фактическим размером раздела /data/, а используют данные в конфигах системы, из-за чего девайс уходит вот в такой софтлок, из которого невозможно выйти обычными средствами. Увы!
Но большинство моих читателей и сами смогут скинуть устройство до заводских настроек через рекавери или просто загуглят, как это сделать. Однако есть и вторая ложка дёгтя — аппаратная, гораздо более серьёзная. Что же это за ложка такая? Узнаем в практической части нашей статьи!
❯ Экономия на чипах памяти
В один прекрасный момент устройство может зависнуть и перезагрузка тут не поможет. Смартфон в это время может перезагружаться, а может и просто висеть, при этом продолжая заряжаться и подавая все признаки рабочего устройства.
Первым делом опытные читатели попытаются его скинуть через рекавери — и даже если не увидят ошибок при вайпах, смартфон всё равно откажется загружаться.
Ещё более опытный пользователь загуглит прошивку для устройства запросом вида «P40 Pro Clone MT6580 firmware» и скачает прошивку для своего девайса с первого попавшегося сайта. По пути, товарищ скачает SP Flash Tool и установит драйверы. Однако при попытке прошить устройство, можно столкнуться с тем, что оно нормально прошилось, но теперь не запускается совсем/зависает с артефактами на экране/перезагружается. А при попытке прошиться ещё раз, увидит вот такое окно:
Увы, теперь Flash Tool не может переразметить внутреннюю память и записать новую таблицу разделов (тот самый GPT) в USER-раздел памяти. Восстановить девайс программными средствами не получится — и это не вина читателя/опытного пользователя.
Происходит это потому что чип флэш-памяти eMMC окончательно износился и ушёл в режим Read-only (только чтение, без возможности записи), дабы пользователь окончательно не потерял свои данные, которые вытащить могут только в СЦ и обычно только со специальным оборудованием. Чип eMMC имеет собственный контроллер, который мониторит физическое состояние секторов памяти и число бэдблоков — эдакий S.M.A.R.T. Когда контроллер решает, что у чипа слишком много испорченных блоков — он частично (т. е. какие-то операции записи всё равно проходят, из-за чего и повреждается GPT) отключает возможность перезаписи данных, из-за чего часть данных уже может быть повреждена, а система начинает вести себя нестабильно — и в случае Android, банально виснуть на заставке.
Иногда контроллер eMMC слишком рано приговаривает всю флэш-память и с помощью специального оборудования (например, z3x) и тестовых пинов на чипе памяти. Насколько мне известно, иногда такие флэши снова «поднимают» с помощью программатора и они могут прослужить ещё долгие годы своим владельцам.
Одним из примеров таких «заумных» контроллеров — eMMC в смартфонах Samsung Galaxy S3, Galaxy S4, Galaxy S4 Mini — у этих девайсов повально «вылетала» память и они висли с теми же симптомами, что описаны в статье.
Ранее проблемами с eMMC страдали флагманские Samsung (из-за ошибки разработчиков), HTC (некачественные чипы памяти) и некоторые другие девайсы. Но почему же эта проблема носит массовый характер на ультрабюджетных смартфонах? Узнаем на примере популярной в 2020 году реплики Huawei P30 Pro на базе чипсета MT6570, которую я купил год назад всего за 400 рублей в состоянии практически новой. Девайс был в коробочке, на нём была заводская плёнка, а его состояние было околоидеальным! Само собой я не удержался прикупить его себе :)
Девайс реально неплохо скопировали, за исключением больших рамок. Дизайн очень симпатичный как по мне. За 400 рублей — отличная покупка, самое время его оживить!
❯ Ремонтируем красавца
Разбирается довольно просто: необходимо отклеить заднюю крышку (осторожно, она очень хрупкая из-за согнутой формы. Я лично, несмотря на всю аккуратность и наличие фена, всё равно её треснул :c. Может у кого есть донор?), используя фен и тонкое лезвие, либо нить. Перед нами открывается вид на плату и аккумулятор:
Теперь нам необходимо снять среднюю часть корпуса, которая держится на винтиках. Не забываем вытащить сим-лоток, задняя часть корпуса (в районе платы с портом зарядки) может отщелкиваться немного туговато — это нормально. Не потеряйте толкатели кнопок — они ничем тут не закреплены.
Теперь можно отпаять АКБ, шлейф с кнопками и отключить шлейф дисплея и нижней платы. Отпаиваем вибромотор и разговорный динамик и вытаскиваем плату:
С обратной стороны чипсет, контроллер питания и флэш-память закрывает металлический экран, который припаян к плате. Его не нужно ничем выдирать, он замечательно сдувается феном на 350 попугаях без нижнего подогрева и риска что-либо угреть. При снятии экрана не заденьте обвязку и не уроните его на чипы, а то потом придется перекатывать КП/проц :)
Обратите внимание на Hynix H9TP32A8JDBC — это eMCP чип памяти, который содержит в одном корпусе 1Гб DDR2 ОЗУ и 8Гб флэш-памяти. Присмотритесь к его маркировке, маркировке на процессоре (MT6570A) и контроллере питания (MT6350V). На чипе памяти она вся в каких-то потертостях и повреждениях. Незадолго до написания статьи, читатель из Краснодара подарил мне еще один клон P30 Pro с идентичной платой и точно такими же проблемами (отвал флэши), там картина была абсолютно идентичная.
Само собой в процессе производства памяти далеко не все чипы получаются рабочими, некоторая часть из них — брак. На заводах есть автоматизированное тестирование, которое относит к бракам ту память, где сектор слишком быстро изнашивается и подвергается ECC (коррекции ошибок) после определенного числа стирания и записи определенных данных.
Из этого мы можем сделать вывод, что производители ради экономии ставят либо Б/У чипы памяти, выпаянные с утилизированных телефонов (H9TP — классическая eMMC, которая использовалась в современных смартфонах более 5 лет назад. Сейчас смартфоны используют UFS), которые уже были изношены на неопределенный процент и могут проработать как много лет, так и пару месяцев, либо отбраковку с заводов, которую, скорее всего, сгружают в общую корзину даже без блистера и затем продают по дешевке производителям смартфонов.
Как же это исправить? Правильно: установить новый чип памяти. Причём необязательно ставить идентичный, подойдет любой, который поддерживается Preloader'ом устройства.
Preloader — первичный загрузчик в смартфонах на базе чипсетов MediaTek, который занимается инициализацией контроллера ОЗУ. Поскольку производителем предполагается использование чипов eMCP — т. е. eMMC и ОЗУ в одном корпусе, в Preloader добавляется поддержка сразу многих чипов памяти. Preloader для выбора конфигурации контроллера ОЗУ использует CID устройства — поэтому, если в нём нет поддержки определенной CID, то устройство откажется включаться. Именно поэтому возникает ошибка DA_LOAD_FAILED при записи неправильного прелоадера!
CID текущей флэши можно узнать в логах SP Flash Tool, а список поддерживаемой памяти конкретным прелоадером можно узнать с помощью программы Wwr MTK.
Наш девайс поддерживает следующие чипы eMMC, оригинальный чип имеет корпус BGA183:
Сдуваем старый чип памяти с помощью фена. Не заденьте проц при снятии, плата теплоемкая, поэтому даже без нижнего подогрева проц будет очень легко «качаться»! После этого снимаем остатки старых шаров с пятачков с помощью оплетки (пятаки здесь крепкие, но если не хотите рисковать — то можно и шарик припоя покатать по площадкам).
Для замены я заказал несколько чипов памяти, точно таких же, как и в оригинале, по 70 рублей каждый, абсолютно новые. Чипы, особенно в такой липкой изнутри упаковке желательно сразу перекатать, иначе могут и не встать с первого раза.
Наносим тонкий слой флюса, дабы чип не уехал, центрируем его и ставим новую флэшу на место при температуре ~330 попугаев. Если флюса нанесено достаточно, то чип не уедет и сам встанет на место — вы сами увидите это. Опытные мастера скажут «ты шо, кукухой поехал? Угреешь же при 330гр.!». Но судя по всему, по настоящему угревается и помирает только уже полумертвая флэша.
Вприпрыжку бежим подсобирать девайс и подключать его к USB. Определится ли девайс? Сможет ли Flash Tool прошить его?
И… да! Флэша встала на место нормально :)
Включаем девайс и не можем нарадоваться: у нас появилось изображение! Правда, девайс всё ещё не грузится: некоторые прошивки сделаны из дампов и не содержат в себе разделов userdata.img и cache.img (обратите внимание на скриншот выше). После замены флэш-памяти, эти разделы оказались забиты нулями и Android не может примонтировать их, из-за чего устройство виснет.
Поправить легко: нужно зайти в рекавери и сделать вайпы. Происходит это из-за того, что некоторые дампы "нарезаны" без раздела userdata и cache. Если они будут забиты нулями, то система не сможет примонтировать эти разделы и зависнет. После перечисленных действий девайс загружается и работает!
❯ Фиксим программные болячки
Однако тут есть важный момент, который я специально предусмотрел для этой статьи: я не делал бэкап NVRAM. Поскольку новая флэша у нас забита нулями и мы прошили только те разделы, которые напрямую связаны с системой, настройки Wi-Fi, Bluetooth и IMEI оказались забиты нулями, из-за чего смартфон даже в 112 не дозвониться. При попытке набрать *#06# мы увидим «Неправильный IMEI», а в Wi-Fi останется сеть с ошибкой NVRAM Warning = 0x10.
Поскольку я предполагаю, что и у вас нет бэкапа NVRAM, то действовать можно следующим путем: попробовать прошить раздел NV от другого устройства на том же чипсете, восстановить NVRAM и IMEI с помощью MetaMAUI с помощью BPLG-файла, а также воспользоваться софтом для СЦ, дабы восстановить IMEI. Девайсы на 6570 и 6580 по каким-то причинам вообще никак не хотят цепляться к в MetaMode, поэтому воспользуемся Miracle Thunder. Нажимаем Ctrl + Shift + M, дабы у нас появились опции для записи IMEI, выбираем IMEI и делаем себе крутые имеи на обе SIM :)
Возвращать нужно только родной IMEI-устройства, который можно найти, например, на сим-лотке. Подделка IMEI является незаконной!
Включаем смартфон, пытаемся позвонить куда-нибудь и ура — всё работает :) Портируем рекавери, накатываем рут — и у нас есть полноценный разлоченный девайс!
❯ Как девайс себя проявляет теперь?
Предлагаю взглянуть на смартфон после того, как мы его оживили! Посмотрим, какие задачи он сможет выполнять, с какой производительностью будет работать и стоило ли его покупать в год выхода — т. е. 2020 году.
CPUID говорит нам о следующих характеристиках и даже здесь его вводят китайцы в заблуждение. ОЗУ никаких не 4Гб, а 1Гб, а флэш-памяти всего 8Гб. Подобные спецификации могут показаться скромными в 2024 году.
Переходим к серфингу сети. И тут уже чувствуется затык по производительности в устройстве. Современные тяжелые сайты девайс откровенно не тянет, а в каких-то ситуациях браузер может даже крашнуться. Увы!
А как же мессенджеры? Ну, буквально на днях «протухли» сертификаты, из-за чего все девайсы до Android 5.1 частично потеряли возможность серфить такие сайты, как, например, ВК. Но и Kate Mobile, и Telegram работают весьма сносно на такой реплике. Тоже самое касается и WhatsApp, однако с тенденциями раз в год-два выкидывать поддержку старых версий Android, мессенджер не проживёт долго на этом девайсе :(
Ну и куда-ж без игр! Залетаем в классику начала десятых и понимаем, что девайс с ней весьма неплохо справляется.
Ну и дабы радовать вас не только скриншотами, прикладываю видео с производительностью работы девайса. Устраивает ли вас такой перформанс? Решать вам!
❯ Заключение
Сегодня мы с вами узнали, каким же именно образом некоторые производители закладывают срок службы в смартфоны. Быстро помирающая флэш-память стала давним бичом даже для флагманских устройств, чего уж говорить о бюджетных китайских репликах. Но как говорят некоторые мастера — девайс всегда можно восстановить, если плата не раскрошена пополам, а я на практике вам подробно рассказал о причинах и показал процесс оживления такого китайчика :)
Стоимость такого ремонта в СЦ составит около 3-4 тысяч рублей, что составляет чуть ли не половину цены нового устройства. Однако не стоит винить мастеров в дороговизне такой операции: новые чипы памяти обычно сами по себе стоят как минимум рублей 400-500, чипы чаще всего перекатывают с заводских шаров на новые, время спецов тоже стоит денег. Моя задача была в том, чтобы доказать вам, что такой девайс можно и дома самостоятельно отремонтировать, имея относительно небольшой набор оборудования. Возможно вы ранее видели мои посты, где я ищу китайские реплики и подделки на айфоны/самсунги/макбуки и т. п. для будущего контента, даже нерабочие. Как вы и сами видите — эти реплики попадают в хорошие руки :)
Если хотите что-то задонатить из подобных девайсов — пишите в тг @monobogdan, буду благодарен и не забуду вас упомянуть в статье!
Стоит ли девайс своих 470 рублей и затраченных сил? Пользуетесь ли китайскими репликами? Пишите в комментариях, будет интересно почитать! Интересен подобный материал? Подписывайтесь на мою телегу: там есть бэкстейдж статей, различные мысли и солюшки по подобному ремонту, а также всегда вовремя публикуются ссылки на статьи!
Ваше мнение насчёт этой статьи?
Ходили ли вы когда-нибудь с подделками на брендовые устройства?
Стоит ли такой смартфон 470 рублей?
Материал подготовлен при поддержке TimeWeb Cloud. Подписывайтесь на меня и @Timeweb.Cloud, дабы не пропускать свежие статьи каждую неделю!
Их есть у нас! Красивая карта, целых три уровня и много жителей, которых надо осчастливить быстрым интернетом. Для этого придется немножко подумать, но оно того стоит: ведь тем, кто дойдет до конца, выдадим красивую награду в профиль!
Если сейчас приехать в пункт приема металлолома, то можно обнаружить просто огромные кучи различных телефонов и прочих электронных «отходов», которые стоят под открытым небом и ждут, когда придёт их черёд окончательного разложения. Однако при ближайшем рассмотрении выясняется, что многие девайсы оказываются полностью рабочими даже после недельного лежания под палящим солнцем и проливными дождями, а сдали их в чермет по причинам «не нужен, надоел, купил новый» и т. п. Я не считаю это правильным, ведь даже в простые кнопочные звонилки имеется возможность вдохнуть новую жизнь, если знать один интересный, но малоизвестный факт: для них можно писать нативные приложения на C и использовать железо телефона в своих целях. А это, на минуточку, как минимум: дисплей с подсветкой, вибромотор, динамик, клавиатура и GSM-радиомодуль с возможностью выхода в сеть. Сегодня мы с вами: узнаем, на каких аппаратных платформах работают китайские телефоны, какие существуют программные платформы и где взять для них SDK, а в практической части мы напишем 2D-игру с нуля, которая будет работать на многих китайских кнопочниках. Интересно? Тогда жду вас под катом!
Содержание:
Не J2ME едины
Аппаратные ресурсы
Кроссплатформенный рантайм
Кроссплатформенный рантайм: Win32
Кроссплатформенный рантайм: MRE
Кроссплатформенный рантайм: VXP
Наконец-то пишем игру
Тестируем на реальных девайсах
Заключение
❯ Не J2ME едины
Думаю, многие мои читатели помнят о такой платформе, как J2ME. Java-приложения стали фактически основной возможностью расширения функционала телефонов в 2000-х годах. API для них был достаточно хорошо стандартизировано, программы не зависели от архитектуры процессора и ОС устройства, а порог вхождения для написания собственных приложений был довольно низкий и даже новички могли за пару дней написать свою игрушку или какое-нибудь GUI-приложение!
Однако не одним J2ME мы были едины: существовало множество платформ, которые так или иначе пытались занять нишу Java на рынке. Некоторые из них я упоминал в своей прошлой статье о написании 3D-игры под Sony Ericsson с нуля: например, была такая платформа на телефонах Sony Ericsson серии T, как Mophun, а CDMA-телефонами с чипсетами Qualcomm использовалась нативная платформа BREW. Пожалуй, я не буду упоминать о .sis и .cab — поскольку это форматы нативных приложений для смартфонов, а не простых «фичефонов».
В какой-то момент, ближе к 2006-2007 году, прилавки российских официальных ритейлеров (по большей части это были телефоны Fly) и неофициальных продавцов на рынках заполонили различные китайские телефоны, которые предлагали какой-то немыслимый функционал для тех лет за копейки, да ещё и визуально напоминали флагманские модели известных брендов. Пожалуй, одним из самых популярных таких телефонов была Nokla TV E71/E72 (да, именно «нокла»), вышедшая примерно в 2008 году и производившаяся аж до 2011 года! За 2-3 тысячи рублей (это менее 100 баксов), пользователь получал здоровый 2.4" дисплей с разрешением 240x320 весьма неплохого качества (когда в те годы многие продолжали ходить с 176x220), да ещё и с тачскрином, гироскоп, огромный громкий динамик (пусть и не очень качественный), поддержку SD-карточек до 32Гб, нередко фронтальную камеру, а также премиальный дизайн с вставками из алюминия. Частенько китайцы заботливо клали в коробку ещё чехольчик и дополнительный аккумулятор :)
Были даже полные копии существующих устройств от Nokia. Особенно китайцы любили подделывать массовые модели на S40: они были очень популярными и китайцы хотели откусить свой кусок рынка у Nokia. Пусть и рынка серого импорта — очевидно, в салонах связи подделки никто не продавал:
Но была и ложка дёгтя в этой бочке меда: китайские телефоны очень часто не имели поддержки Java, из-за чего многие пользователи разочаровывались в них из-за отсутствия возможности установить необходимые им приложения. Никакой тебе оперы, аськи, игр… Скорее всего, это связано с необходимостью отчислений Sun, а также разработчикам реализации J2ME-машины (JBed/JBlend) и установки чипа флэш-памяти чуть большего объёма.
Но многие пользователи не знали, что такие девайсы не просто поддерживали сторонние приложения, но и умели выполнять настоящие нативные программы, написанные на полноценном C! Всему помешала китайская костыльность и тотальная закрытость. Платформа предполагалась для работы на внутреннем рынке. Для вызова менеджера нативных приложений необходимо было вводить специальный инженерный код в номеронабирателе, предварительно скопировав приложение в нужную папку, а SDK долгое время было платным и доступно только для компаний из Китая. Кроме того, далеко не все приложения могли запустить на конкретном девайсе — были серьезные проблемы с совместимостью.
Всё как вы любите: HiTech-девайсы на фоне ковра, который старше автора лет на 30 :)
В ранних китайских телефонах использовалась платформа Mythroad (MRP, MiniJ) от китайской компании SkyWorks, которая лицензировала свою технологию производителям чипсетов. Поддержку MRP можно было встретить на телефонах с чипсетами MediaTek, Spreadtrum, а также MStar (и возможно Coolsand). Mythroad предоставлял некоторое API для работы с железом телефона и разработки как UI-приложений, так и игр, кроме того, Mythroad позволял хранить ресурсы в одном бинарнике с основной программой и даже имел какой-то интерпретируемый язык помимо возможности запуска нативного кода. Для работы таких приложений необходимо было скопировать менеджер приложений dsm_gm.mrp и игру в папку mythroad во внутренней памяти устройства или на флэшке, а затем набрать в номеронабирателе код *#220807#, иногда при отключенной первой SIM-карте. Костыльно? Костыльно! Откуда об этом знать среднестатистическому пользователю? Не откуда! Но работало!
Эта платформа поддерживалась на большинстве подделок под брендовые устройства Nokia, Sony Ericsson и Samsung, а также iPhone и на многих китайских кнопочных телефонах 2008-2010 годов.
Ближе к 2010 году MediaTek разработала свою собственную платформу, которая должна была заменить MRP — WRE (VXP). Эта платформа была гораздо шире с точки зрения функционала (например, был доступ к UART) и её API был вполне удобно читаем для программиста, а SDK свободно доступен для всех. Один нюанс всё портил — приложения без подписи привязывались к IMSI (даже не IMEI) симки в девайсе и на некоторых девайсах требовали переподписания под каждую конкретную SIM или патчинг дампа оригинальной прошивки телефона на отключение проверки подписи. Эта платформа поддерживалась на многих кнопочниках и смарт-часиках 2010-2020 годов: к ним относятся новодельные телефоны Nokia, телефоны DNS и DEXP, Explay и т. п. Для запуска приложений достаточно было выбрать файл с разрешением VXP в проводнике и просто запустить его. Но с совместимостью всё равно имелись проблемы: если запустить VXP для версии 2.0 и выше, мы получим лишь белый экран. Ну хоть не софтресет, и на том спасибо!
Далеко не все такие часы поддерживают MRE, смотреть нужно от устройства к устройству
❯ Аппаратные ресурсы
Большинство китайских кнопочных телефонов работает на базе одних и тех же чипсетов. В конце нулевых чаще всего использовались чипсеты MT6225, SC6520 и некоторые чипы от Coolsand. Средние хар-ки девайса были следующими:
Процессор: ARMv5 ядро на частоте ~104МГц, ARM926EJ-S. Нет FPU, есть Thumb. Большую часть процессорного времени программа могла забрать себе.
ОЗУ: ~4Мб SDRAM. Программам было доступно 512Кб-1Мб Heap'а. Это, в целом, довольно немало для большинства применений.
Флэш-память: ~32Мб, пользователю доступно пару сотен килобайт. Да, вы не ослышались, килобайт! Однако можно без проблем использовать MicroSD-флэшки до 32Гб.
Дисплей: от 128x128 до 320x480, почти всегда есть 18-битный цвет (262.000 цветов), в случае TV E71/E72 используется очень неплохая TN-матрица с хорошими углами обзора и яркой подсветкой. Иногда есть тачскрин.
Звук: громкий динамик, наушники.
Аккумулятор: ~800мАч, на некоторых девайсах может быть и 2.000мАч, а то и больше!
Ввод: клавиатура, иногда была поддержка QWERTY.
Внешние шины: почти всегда был доступен UART, причём его можно было свободно взять прямо с платы — он был явно подмечен! Взять GPIO с проца не выйдет (кроме, возможно, вибромотора), SPI и I2C также напрямую недоступны. Внешние шины можно реализовать с помощью UART через GPIO-мост из микроконтроллера.
В итоге мы получаем очень неплохие характеристики для устройства, которое сочетает в себе сразу всё. На базе такого девайса можно сделать и сигнализацию, и HMI-дисплей с интерфейсом для управления каким-нибудь устройством, и игровую консоль с эмуляторами… да на что фантазии хватает! И это за какие-то 200-300 рублей, если мы говорим о б/у устройстве или 600 рублей, если говорим о новом. Это дешевле, чем собирать девайс с подобным функционалом самому из готового МК (например, RP2040) и отдельных модулей. Кстати, дешевые 2.4" дисплеи на алике — это ни что иное, как невостребованные остатки дисплеев для подобных китайских телефонов на складах! А вы думали, откуда там значки на тачскрине снизу?
Однако в рамках данной статьи мы не будем ограничиваться лишь теорией и на практике напишем примитивную 2D-игрушку, которая будет работать сразу на трех платформах без каких-либо изменений в коде самой игры: Windows, MRP (Mythroad) и VXP. Но для того, чтобы достигнуть такого уровня абстракции от платформы, нам необходимо написать рантайм, который оборачивает все необходимые платформозависимые функции для нашей игры.
Игрушка будет простой: 2D скролл-шутер с видом сверху, а-ля Asteroids. Летаем по космосу, и стреляем по враждебным корабликам, стараясь не попасть под вражеские лазеры. Всё просто и понятно :)
❯ Практическая часть: Кроссплатформенный рантайм
Итак, что нам необходимо от абстракции для такой простой игры? Давайте посмотрим:
Графика: очистка экрана, отрисовка спрайтов с прозрачностью (без альфа-блендинга, только колоркей), отрисовка текста. При возможности, желательно использовать нативное API системы для рисования графики, а не городить собственный блиттер. Формат пикселя фиксирован: RGB565 (65к цветов).
Ресурсы: хранятся в одном образе с основной игрой. Фактически, все ресурсы упакованы в виде обычных массивов байт в заголовочных файлах. Я пользуюсь вот этой тулзой для конвертации спрайтов в массивы байтов.
Звук: воспроизведение хотя-бы одного WAV-потока. Почему одного? Потому что далеко не на всех платформах есть доступ к аппаратному микшеру… да и вообще не везде есть прямой доступ к PCM (привет MRP), иногда разработчики ограничиваются лишь одним каналом для WAV-звука без возможности воспроизведения нескольких аудиофайлов одновременно.
Ввод: абстракция от клавиатуры классического моноблока: стрелки, OK, левый и правые софткеи.
Стандартная библиотека: не на всех платформах можно вызывать функции напрямую из stdlib. Как минимум в MRP и, например, «эльфах» для Motorola, нет возможности вызывать аллокатор, rand и некоторые другие функции из обычных заголовочников стандартной библиотеки. На таких платформах, системные инклуды дефайнами подменяют стандартные функции на своих реализации:
#define malloc system_alloc
#define free system_free
Но если у нас игра кроссплатформенная, то и платформозависимые инклуды мы использовать не будем.
Выглядит всё достаточно просто, верно? Примерно такого набора функций хватит для нашей игры:
❯ Win32
Давайте же перейдем к реализации рантайма на каждой платформе по отдельности. Начнём с Win32, поскольку адекватно отлаживать игру можно только на ПК.
На десктопе у нас будет фиксированное окно 240x320, в качестве GAPI будет использоваться аппаратно-ускоренный OpenGL, а для обработки ввода будет использоваться классически GetAsyncKeyState. Реализация точки входа, создания окна и инициализации контекста GL и главного цикла приложения у нас такая:
Реализация отрисовки спрайтов очень примитивная — OGL 1.0, полностью FFP, вся отрисовка — это 2 треугольника, формирующие квад. Спрайт заливается при первом использовании в текстуру, последующие кадры реюзается уже готовая текстура. Фактическая реализация всего рендерера — т. е. функций для рисования «просто картинок», без поддержки атласов, блендинга цветов (З.Ы - длинные листинги будут на пастбине, на Пикабу нет нормального тега для кода):
С вводом тоже всё просто. Есть биндинг кнопок клавиатуры к кнопкам на кейпаде телефона. inGetKeyState предполагается вызывать один раз за кадр, поэтому функция опрашивает ОС о состоянии нажатых кнопок на клавиатуре и назначает состояние виртуальных кнопок относительно состояния физических кнопок на клавиатуре.
Результат:
❯ MiniJ
Переходим к реализации рантайма для первой китайской платформы — MRP. Обратите внимание — я использую нативное API платформы для рисования спрайтов. Связано это с тем, что софтварный блиттер работает невероятно медленно даже с прямым доступом к скринбуферу устройства, а в чипсете предусмотрена отдельная графическая подсистема с командбуфером для быстрой отрисовки примитивов и графики:
SDK для MRE можно найти здесь (SKYSDK.zip): оно уже пропатчено от необходимости покупки лицензии. MRP не развивается более 10 лет, поэтому, думаю, его можно считать Abandonware. Компилятор находится в compiler/mrpbuilder.NET1.exe. За китайские SDK в публичном доступе нужно поблагодарить пользователя 4pda AjlekcaHgp MejlbHukoB, который раздобыл их на всяких csdn и выложил в свободный доступ :)
У MRP собственная система сборки, основанная на конфигурациях. Поскольку MRP может работать на устройствах с разными платформами и размерами дисплеев, под каждую можно настроить свой конфиг, который пережмет ресурсы в нужный формат. Дабы ничего не ломать, я заюзал абсолютные пути:
Компиляция приложения:
mrpbuilder.net1.exe game.mpr
Начинаем с функций обработки событий и инициализации, которые вызывает рантайм при старте приложения: mrc_init вызывается при старте приложения, а mrc_event при возникновении события. Вся инициализация очень простая: создаём таймер для обновления и перерисовки состояния игры и вызываем инициализацию игры:
С вводом тоже никаких проблем нет, нажатия кнопок прилетают как события в mrc_event. Переводим кейкоды MRE в наши кейкоды и сохраняем их состояние:
Опять же, отлаживать MRP-приложение под реальным устройством проблематично, поэтому платформозависимый код должен быть минимальным. Кроме того, обратите внимание, что некоторые функции в MRP зависят от библиотек-плагинов. Линкер слинкует вашу программу, но на реальном устройстве их вызов вывалится в SIGSEGV и софтресет устройства. Также нельзя использовать ничего из стандартной библиотеки именно в стандартных заголовочниках (т. е. stdlib.h, string.h и т. д.), часть стандартной библиотеки реализовывается MRP и дефайнится в mrc_base.h
Что интересно, защиты памяти толком нет. Если приложение падает в SIGSEGV или портит память — систему, судя по всему, ребутит Watchdog. Защиты памяти никакой, можно напрямую читать и писать в память ядра, а также писать в регистры периферии чипсета. jpegqs, покумекаем над этим? :)
Переходим к рендереру. Тут буквально две функции, gClearScreen очищает экран, а gDrawBitmap рисует произвольный спрайт с форматом пикселя RGB565. В качестве ROP используется BM_TRANSPARENT — таким образом, mrc_bitmapShowEx будет использовать левый верхний пиксель в качестве референсного цвета для реализации прозрачности без альфа-блендинга.
voidgDrawBitmap(CBitmap* bmp, int x, int y) {
mrc_bitmapShowEx((uint16*)bmp->pixels, x, y, bmp->width, bmp->width, bmp->height, BM_TRANSPARENT, 0, 0);
}
Да, всё вот так просто. Рантайм теперь запускается на реальных китайских девайсах и работает стабильно.
❯ VXP
Теперь переходим к VXP — платформе не менее неоднозначной, чем MRP. Пожалуй, начать стоит с того, что VXP существует аж в трёх версиях: MRE 1.0, MRE 2.0 и MRE 3.0. В MRE 2.0 и выше появилась поддержка плюсов (в MRE 1.0 только Plain C) и довольно интересного GUI-фреймворка, MRE 1.0 же предлагает реализовывать гуй самому. Платформа распространена на большинстве кнопочных телефонов и смарт-часиков на чипсетах MediaTek, примерно начиная с 6235 и заканчивания 6261D. SDK можно скачать вот здесь (см MRE_SDK_3.0).
VXP сам по себе более функционален чем MRE, поскольку ориентирован исключительно на телефоны с чипсетами MediaTek. Но что самое приятное — есть доступ к уарту без каких либо костылей! То есть, если сделать GPIO-мост на условной ESP32, то мы можем получить готовый мощный МК с клавиатурой, кнопками, дисплеем, звуком и т. д. Звучит не хило, да? Кроме того, у нас есть доступ и к BT, и к GPRS, и к SMS без каких либо ограничений.
Однако в бочке мёда нашлась и ложка дёгтя: для компиляции MRE-приложений необходимо накатывать и крякать довольно старый компилятор ADS, который сам по себе поддерживает только C89 (например, нет возможности объявить переменную в объявлении цикла или середине функции, только в начале, как в Pascal). ADS уже вроде как Abandonware, так что это вроде не наказуемо… но всё равно неприятно.
Кроме того, на некоторых девайсах (в основном, фирменных Nokia а-ля 225), прошивка требует подписи у всех бинарников, либо если бинарник отладочный, то должна быть привязка к конкретному IMSI.
К тому же, каждая программа должна фиксированно указывать в заголовке, сколько Heap-памяти ей необходимо выделить. Оптимальный вариант — ~500Кб, тогда приложение запустится вообще на всех MRE-телефонах.
Зато у VXP есть адекватный симулятор под Windows. Но зачем он нам, если у нас порт игры под Win32 есть? :)
Начинаем с инициализации приложения. В процессе вызова точки входа, приложение должно назначить обработчики системных событий, коих бывает несколько. Для обработки ввода и базовых событий хватает всего три: sysevt (события окна), keyboard (физическая клавиатура. Есть полная поддержка QWERTY-клавиатур), pen (тачскрин).
Переходим к обработчику системных событий. Обратите внимание, что MRE-приложения могут работать в фоне, из-за чего необходимо ответственно подходить к созданию и освобождению объектов. Что важно усвоить с самого начала — в MRE нет понятия процессов и защиты памяти, как на ПК и полноценных смартфонах. Любая программа может попортить память или стек ОС, более того, программа использует аллокатор остальной системы, поэтому если ваша программа не «убирает» после себя, данные останутся в памяти со временем приведут к зависанию. Впрочем, WatchDog делает свою работу быстро и приводит телефон в чувство (софтресетом) за 1-2 секунды. Но как и в случае с MRE, есть приятный бонус: прямой доступ к регистрам чипсета :)
Переходим к обработке событий с кнопок. Тут всё абсолютно также, как и на MRE, лишь имена дейфанов поменялись :)
И наконец-то, к графике! Пожалуй, стоит сразу отметить, что более 20-30 FPS на большинстве устройств вы не получите даже с прямым доступом к фреймбуферу. Похоже, это связано с тем, что в MRE довольно замороченная графическая подсистема с поддержкой альфа-канала (только фиксированного во время вызова функции отрисовки картинки/примитивов, сам пиксельформат всегда RGB565) и нескольких слоев. Кроме того, похоже есть ограничения со стороны контроллера дисплея.
Изначально, MRE предполагает то, что все картинки в программе хранятся в формате… GIF. Да, весьма необычный выбор. Однако для работы с пользовательской графикой, есть возможность блиттить произвольные картинки напрямую из RAM. Вот только один нюанс — посмотрите внимательно не объявление следующей функции:
dst_disp_buf — это целевой RGB565-буфер. Логично предположить, что и src_disp_buf — тоже обычный RGB565-буфер! Но как бы не так. Документация крайне скудная, пришлось посидеть и покумекать, откуда в обычном 565 буфере возьмется индекс кадра. С подсказкой пришёл пользователь 4pda Ximik_Boda — он скинул структуру-заголовок, которая идёт перед началом каждого кадра. В документации об этом не сказано ровным счетом ничего!
Сначала я реализовал софтовый блиттинг, но он безбожно лагал. Мне стало интересно, почему нативный blt быстрее и… вопросы отпали после того, как я поглядел в ДШ чипсета: тут есть аппаратный блиттинг. И даже с ним девайс не может выдать более 20FPS!
Для реализации более-менее шустрого вывода графики, необходимо сначала создать канвас (фактически, Bitmap в MRE), создать и привязать к нему layer, получить указатель на буфер слоя и только потом скопировать туда нашу картинку. Да, вот так вот замороченно:
И только после этого всё заработало достаточно шустро :) В остальном же платформа довольно неплохая. Да, без болячек не обошлось, но всё же перспективы вполне себе есть.
На данный момент, этого достаточно для нашей игры.
❯ Пишем геймплей
Рантайм у нас есть, а значит, можно начинать писать игрушку. Хоть пишем мы на Plain-C, я всё равно из проекта в проект использую +- одну и ту же архитектуру относительно системы сущностей, стейтов и т. п. Поэтому центральным объектом у нас станет CWorld, который хранит в себе на пулы с указателями на другие объектами в сцене, а также игрока и его состояние:
Система стейтов простая и понятная — фактически, между состояниями передавать ничего не нужно. При нажатии в главном меню на «старт», нам просто необходимо проинициализировать мир заново и начать геймплей, при смерти игрока — закинуть его обратно в состояние меню. Стейты представляют из себя три указателя на функции: переход (инициализация), обновление и отрисовка.
typedefvoid(CGameStateCallback)();
Поскольку мы хотим некоторой гибкости при создании новых классов противников, то вводим структуру CEnemyClass, которая описывает визуальную составляющую врагов и их флаги — могут ли они стрелять по игроку или просто летят вниз (астероиды), как они передвигаются (зигзагами например) и т. п.
Всё! Для текущего уровня реализации игры этого достаточно :) Переходим к реализации игровой логики. Вообще, динамический аллокатор в играх для китайских платформ лучше использовать как можно меньше. Heap'а довольно мало (~600Кб), да и не совсем понятно, как этот аллокатор реализован, есть вероятность, что используется аллокатор и куча основной ОС.
Начинаем с реализации полёта кораблика. Для этого он должен реагировать на стрелки и не улетать за границы экрана, а ещё для красоты он должен «вылетать» из нижней границы экрана при старте игры:
Переходим к динамическим пулам с объектами. Как вы уже заметили, их всего два — враги и летящие снаряды. Реализация спавна врагов/снарядов простая и понятная: мы обходим каждый элемент пула, если указатель на объект не-нулевой, значит объект всё ещё жив и используется на сцене. Если нулевой — значит ячейка свободна и можно заспавнить новый объект:
При обходе пула во время обновления кадра, мы обновляем состояние каждого объекта и если его функция Think вернула true, значит объект больше не нужен и его нужно удалить:
if (enemyThink(world.enemyPool[i]))
{
sysFree(world.enemyPool[i]);
world.enemyPool[i] = 0;
}
А вот и реализация Think:
boolenemyThink(CEnemy* enemy) {
enemy->y += enemy->_class->speed;
if (enemy->y > gGetScreenHeight() || enemy->health <= 0) return true;
return false;
}
Но кораблики должны же откуда-то появляться! Для этого у нас есть переменная nextSpawn, которая позволяет реализовать самый простой тип спавнера — относительно времени (или в нашем случае тиков):
world.nextSpawn--;
if (world.nextSpawn < 0) {
CEnemy* enemy = spawnEnemy(&enemyClasses[0]);
world.nextSpawn = randRange(40, 70);
}
Результат: мы уже можем полетать, пострелять и поуворачиваться от вражеских корабликов!
Уже что-то напоминающее игру! Осталось лишь добавить подсчет очков, менюшку, разные виды противников, возможно какие-то бонусы и у нас будет готовая простенькая аркада. В целом, выше приведена достаточно неплохая архитектура для простых 2D-игр на Plain C. Фактически, она может быть хорошей базой и для ваших игр: в теме о китах на 4pda я встречал немало людей, которые банально не знали, с чего начать.
❯ Что у нас получилось?
Но без тестов на реальных устройствах материал не был бы таким интересным! Поэтому давайте протестируем игру на двух реальных телефонах, как вы уже догадались, один — Nokla TV E71, а второй — клон Nokia 6700, который подарил мне мой читатель Никита.
На TV E71 игра идёт не сказать что очень бодро. Кадров 15 точно есть, что, учитывая разрешение 240x320, весьма неплохо для такого девайса.
а 6700,, даже учитывая более низкое разрешение — 176x220, дела примерно также — ~15FPS! Но поиграть всё равно можно. Уже хотите написать «автор наговнокодил, а теперь ноет из-за низкого FPS»? Ан-нет, я попробовал игры сторонних разработчиков — они идут примерно также :( К сожалению, таковы аппаратные ограничения устройства.
Исходный код игры с Makefile'ами и файлами проектов для Visual Studio и MRELauncher доступны на моём GitHub. Свободно изучайте и используйте его в любых целях :)
❯ Заключение
Но в остальном же, демка получилась довольно прикольной, как и сам опыт программирования для китайских телефонов. В общем и целом, китайцы пытались максимально упростить API и привлечь разработчиков к своей платформе. Если ради примера взглянуть на API для Elf'ов на Motorola, можно ужаснуться от state-based архитектуры платформы P2K. А тут тебе init, event, draw — и всё!
Но популярности помешала непонятная закрытость платформы, костыльный запуск программ, отсутствие нормального симулятора. А ведь сколько фишек было: даже возможность писать и читать память ядра! А вы как считаете? Можно ли вдохнуть в китайские кнопочники новую жизнь, узнав о наличии возможности запуска нативного кода на них?
P. S.: Друзья! Время от времени я пишу пост о поиске различных китайских девайсов (подделок, реплик, закосов на айфоны, самсунги, сони, HTC и т. п.) для будущих статей. Однако очень часто читатели пишут «где ж ты был месяц назад, мешок таких выбросил!», поэтому я решил в заключение каждой статьи вставлять объявление о поиске девайсов для контента. Есть желание что-то выкинуть или отправить в чермет? Даже нерабочую «невключайку» или полурабочую? А может, у этих девайсов есть шанс на более интересное существование! Смотрите в соответствующем посте, что я делаю с китайскими подделками на айфоны, самсунги, макбуки и айпады! Да и чего уж там говорить: эта статья уже сама по себе весьма наглядный пример! Найти меня можно в комментариях тут, на Пикабу, и в тг @monobogdan
Понравился материал? У меня есть канал в Телеге, куда я публикую бэкстейдж со статей, всякие мысли и советы касательно ремонта и программирования под различные девайсы, а также вовремя публикую ссылки на свои новые статьи. 1-2 поста в день, никакого мусора!
Полезный материал?
Были ли у вас такие китайчики?
Материал подготовлен при поддержке TimeWeb Cloud. Подписывайтесь на меня и @Timeweb.Cloud, дабы не пропускать новые статьи каждую неделю!
Привет! Вероятно, половина обитателей Пикабу так или иначе уже видели и читали мои статьи - я пишу о подручном ремонте, моддинге, программировании и использовании гаджетов прошлых лет. Довольно большого количества статей возможно и не вышло бы без вашей помощи - именно читатели помогают мне найти некоторые достаточно экзотические гаджеты, про которые я готовлю подробный материал.
Прямо сейчас я составил небольшой список девайсов, которые могут быть вам не нужны, но которые были бы интересны для оживления и будущего контента. не стесняйтесь писать в комментах, если у вас есть что-то подобное, а то часто бывает что начинаю искать какой-то гаджет, а люди пишут "где ж ты был месяц назад! Я целый грузовик их выкинул!" :(
Китайские реплики флагманских и дорогих смартфонов начала 2010х годов. Сюда относятся китайские айфоны 4/4s/5/5s/5c/6/6s, galaxy s2/s3/s4/s5/mega/note, htc one x, подделки на Lumia и.т.п. Работали эти реплики на подрисованном в эппловскую систему Android'е и обычно не очень шустро.
Если у кого-то хорошая память на бренды, то вот известные китайцы: ORRO (именно так, не OPPO), SciPhone, Feiteng, HTM, Vinko, BML. Если ваш друг или знакомый когда-то торговал подобными гаджетами (почти в каждом городе были рынки с такими "серыми" телефонами) и ему приносили бракованные подобные девайсы - тоже можно скинуть контакт, поговорить, возможно куплю болячки там обычно не существенные.
Довольно ранние реплики на винде и самых первых андроидах (1.5-1.6). Про них большинство забыли за давностью лет:
И максимальная дичь - реплики айпадов, макбуков и прочей техники Apple. Да, я люблю подобные девайсы собирать в эдаку экосистему))
К огромному сожалению, старые смартфоны всё чаще и чаще находят своё пристанище в мусорном баке. К прошлым, надежным «друзьям» действует исключительно потребительское отношение — чуть устарел и сразу выкинули, словно это ненужный мусор. И ведь люди даже не хотят попытаться придумать какое-либо применение гаджетам прошлых лет! Отчасти, это вина корпораций — Google намеренно тормозит и добивает довольно шустрые девайсы. Отчасти — вина программистов, которые преследуют исключительно бизнес-задачи и не думают об оптимизации приложений совсем. В один день я почувствовал себя Тайлером Дёрденом от мира IT и решил бросить вызов проприетарщине: написать свою прошивку для уже существующего смартфона с нуля. А дабы задачка была ещё интереснее, я выбрал очень распространенную и дешевую модель из 2012 года — Fly IQ245 (цена на барахолках — 200-300 рублей). Кроме того, у этого телефона есть сразу несколько внешних шин, к которым можно подключить компьютер или микроконтроллер, что даёт возможность использовать его в качестве ультрадешевого одноплатника для DIY-проектов. Получилось ли у меня реализовать свои хотелки? Читайте в статье!
Мотивация
Честно сказать, идея попытаться реализовать свою прошивку мне пришла ещё давно. Однако, дабы не завлекать опытного читателя кликбейтом, я сразу поясню, в чём заключается «прошивка с нуля»:
Мы всё ещё используем Linux: в качестве ядра мы продолжаем использовать образ Linux, предоставленный нам производителем. Написание прошивки полностью с нуля заняло бы очень много времени (особенно без схемы на устройство). Однако, мы вообще не загружаем Android никаким образом.
Мы не используем библиотеки AOSP: наша прошивка без необходимости не использует никаких библиотек уже имеющегося образа Android. Вся работа с железом происходит с помощью низкоуровневого API Linux. Это значит, что отрисовка графики, звук, управление ресурсами и питанием ложится полностью на нас.
Прошивка может запускать только нативные программы: да, это тоже камень в сторону Android. Изначально, наша прошивка умеет запускать только нативные программы, написанные на C. Причём она экспортирует собственное C API — дабы приложения могли использовать всю мощь нашего смартфона в виде простого и понятного набора методов.
Проектов по выкидыванию Android из, собственно, Android-смартфонов как минимум несколько: UBPorts — бывший Ubuntu Touch, FireFox OS и его наследник Kai OS и конечно же, postmarketOS. Отчасти можно сюда отнести и Sailfish OS — но там образы имеются в основном на смартфоны от Sony. Все эти проекты объединяет сложность портирования и невозможность их завести на устройствах без исходного кода ядра. Даже если у вас есть исходный код ядра, но, например, устройство использует ядро 2.6 — навряд-ли вы сможете завести современный дистрибутив на нём.
Другой вопрос в том, что можно использовать полу-baremetal подход, когда от Linux берется практически минимальный функционал. Всё, что мы имеем — busybox, libc и низкоуровневый доступ к железу, благодаря API самого ядра. Как под это всё программировать — я рассказывал впрошлойстатье. Этот же подход мы будем использовать и сейчас — как иллюстрация реального применения подобного способа.
Итак, что наша прошивка должна уметь:
Отрисовывать произвольную графику: графическая подсистема нашей прошивки должна работать с фиксированным форматом пикселя, уметь загружать прозрачные и непрозрачные изображения, отрисовывать картинки с альфа-блендингом и т. п.
Уметь звонить и работать с модемом: общение с модемом происходит посредством AT-команд — общепринятого в индустрии стандарта. Однако в случае нашего устройства, есть м-а-а-а-ленький нюанс, о котором я расскажу позже.
Иметь механизм приложений: мы ведь не будем хардкодить все «экраны» в прошивке в виде кучи стейтов, верно? Для этого у нас должен быть простой и понятный механизм слинкованных с прошивкой приложений.
Обрабатывать ввод: обработка тачскрина и жестов — это задача подсистемы ввода.
Реализовывать анимированный UI: здесь всё очевидно, наша прошивка должна иметь готовые элементы пользовательского интерфейса для будущих приложений: кнопки, текстовые поля и т. д. О деталях реализации этой подсистемы, я расскажу ниже (а реализовал я её очень необычно для такой системы).
Начинаем мы с хардварной части. Именно здесь я покажу вам, как использовать внешние шины вашего устройства.
Аппаратная часть
В качестве смартфона для нашего проекта, я выбрал популярную бюджетную модель из 2012 года — Fly IQ245 Wizard. Это простенький китайский смартфон, который работал на базе популярного в прошлом 2G-чипсета: MediaTek MT6573, да и стоил около 2х тысяч рублей новым. Однако вот в чём суть: мне удалось заставить работать «медиатековский» модем и даже позвонить с него на свой основной телефон, но… только ввод и вывод данных из звукового тракта модема происходит через звуковую подсистему Android — к которой доступа у нас нет!
Именно поэтому, мы идём на очень хитрый и занимательный костыль: мы распаяем внешний модем сами! В качестве радиомодуля у нас выступит модуль SIM800 от компании SIMCOM. И даже он очень близок к нашему смартфону в аппаратном плане: ведь в основе этого модуля лежит популярнейший чипсет из кнопочников тех лет: MediaTek MT6261D. Преимущество SIM800 в его цене — он стоит пару сотен рублей, так что по карману выбор модема не влияет.
На весу паять крайне неудобно. В финальном варианте перепаяю нормально.
Но как его подключать? SIM800 общается с другими устройствами посредством протокола UART — универсальный асинхронный приемо-передатчик. И вот тут мы включаем смекалочку. Разбираем устройство и видим то, что я пытаюсь долгое время донести до моих читателей — аж два канала UART: один практически посередине, второй справа. Нам нужны пятачки TXD4 и RXD4:
Обычно на этот канал UART летят логи ядра, которые можно без проблем отключить минорной правкой U-Boot в HEX-редакторе. Впрочем, модем никак не реагирует на «мусор» из консоли и просто отвечает ошибками — хватит лишь очистить буфер сообщений для того, чтобы все работало нормально. Подпаиваемся к UART'у с помощью преобразователя — у меня оным выступает ESP32 с выпаянным чипом.
Увидели логи? Замечательно, пора попытаться что-то отправить на ПК и с ПК. UART работают без тактовых сигналов и зависит исключительно от старт/стоп битов и бодрейта, который на устройствах MediaTek равен 921600. TXD4 и RXD4 обнаруживаются в системе на консоли/dev/ttyMT3. Пробуем что-то отправить: всё работает!
Вот теперь-то можно подключить наш внешний модем и попытаться пообщаться с ним, отправив тестовую командуAT. Модем отвечаетOK! На этот раз я работаю с смартфоном из режимаFactory mode— практически тоже самое, что и режим recovery, но позволяющий, например, получить доступ к камере устройства. Простая и понятная схема, поясняющая что и куда подключать:
На этом модификация аппаратной частипоказакончена. Пора переходить к реализации софта! Я решил разделить материал на каждый модуль, который я реализовывал — дабы вам был понятен процесс разработки и отладки прошивки!
Заставляем смартфон запускать нашу прошивку
На этот раз я решил загружать смартфон из режима рекавери. Однако никто не мешает в будущем просто прошить раздел recovery вместо boot и получить прямую загрузку прямо в нашу прошивку. Время такой загрузки будет заниматься ~3-4 секунды с холодного старта. Очень даже ничего.
Я взял уже готовый образ TWRP для своего смартфона и пропатчил его, дабы сам рекавери не мешал своим интерфейсом. Для этого я распаковал образ recovery.img с помощью MtkImgTools и убрал в init.rc запуск службы /sbin/recovery. После этого, я залил прошивку обратно на устройство и получил подобную свободу действий — консоль через USB и чистый холст в виде смартфона! Старые смартфоны на чипсетах MediaTek шьются через USB только после замыкания тест-поинта — на моем аппарате его местонахождение очевидно. Замыкаем контакты между собой, подключаем смартфон без АКБ к ПК и ждем прошивки:
Теперь можно деплоить программы! Важный нюанс: в отличии от Makefile из прошлой статьи, для Android 2.3 параметр -fPIE нужно убрать — иначе динамический линкер (/sbin/linker) будет вылетать в segmentation fault.
❯ Графическая подсистема
В комментариях под прошлой статьёй меня похвалили за то, что я делюсь достаточно профильными знаниями касательно эффективной отрисовки 2D-графики. Собственно, к реализации графической подсистемы я подошёл ответственно и постарался реализовать достаточно шустрый рендерер, к которому затем можно подключить другие модули.
Как я уже говорил ранее, графическая подсистема должна уметь загружать картинки, выводить некоторые примитивы, выводить картинки с прозрачностью и без, загружать и отрисовывать заранее подготовленные шрифты, а также управлять отрисовкой бэкбуфера на экран.
В случае с этим устройством (и большинством старых устройств), формат пикселя оказался RGB565 — т. е. 5 бит красный, 6 бит синий, 5 бит зеленый. Конвертация форматов пикселей всегда была занозой в заднице для программных рендереров, поскольку занимает дополнительное время, которое обратно зависимо от размера дисплея. Изначально я решил выделить буфер в том же формате, что и фреймбуфер, но затем решил сделать классический и самый портативный формат — RGB888 (24х-битный цвет), а при копировании кадра на экран, на лету делать преобразования цвета:
Очень важный нюанс, который я не упомянул в предыдущей статье: на устройствах прошлых лет для обновления фреймбуфера необходимо послать структуру var_screeninfo, где хотя бы что-то изменено, иначе никаких изменений мы не увидим. Этот же костыль используется в родном recovery для отрисовки, а судя по исходникам драйвера fb, «правильный» способ обновить экран — послать драйверу ioctl (который я пока что не пробовал).
После того, как я смог управлять дисплеем, я решил загрузить и отобразить какую-нибудь картинку. Пусть это будут обои для нашей прошивки:
Загрузчик TGA сильно не поменялся: я таскаю его в неизменном виде из проекта в проект. Он поддерживает любые форматы пикселя, кроме палитровых, но я его искусственн ограничиваю на RGB888 и RGBA8888 — для поддержки обычных картинок и картинок с альфа-каналом. После этого, я написал не очень шустрые, но достаточно универсальные методы для отрисовки картинок. Для больших участков кода, я буду использовать pastebin, поскольку на Пикабу до сих пор не добавили ни подсветки синтаксиса, не нормальный перенос форматирования табов :(
PutPixel желательно заинлайнить в будущем. В целом, сама отрисовка работает достаточно быстро, но поскольку рендеринг выполняется на ЦПУ — рано или поздно мы упремся в количество картинок на экране. Есть некоторые оптимизации: например, непрозрачные картинки можно просто коприовать сканлайнами прямо в задний буфер.
Сразу же реализовываем методы для рисования шрифтов: они у нас будут совсем простенькими — только моноширинные (все символы имеют одинаковую ширину) и растровыми (для каждого размера придется «запекать» несколько шрифтов). Для этого я написал маленькую программку, которая рисует виндовые шрифты прямо в наш самопальный формат:
Формат примитивнейший:
1 байт говорит нам о размере шрифта и далее идут 255 изображений символов. Да, это не очень эффективно т.к попадают пустые символы из ASCII-таблицы, но в будущем это можно поправить.
Прозрачность в символах обеспечивает фоновый цвет Magena — ярко-розовый. Я не стал делать дополнительный альфа-канал, т. к. иначе будут серьезные лаги при выводе большого количества текста.
Теперь у нас есть отображение картинок и текста! Что с этим можно сделать?
❯ Обработка ввода
Пока что здесь не хватает обработки «хардварных» кнопок — домой, меню, назад и т. п. Однако в будущем это всё можно реализовать!
❯ Анимация
Не забыл я и про анимации. Ну кому с такими ресурсами нужен неанимированный топорный интерфейс? Пусть лучше будет анимированный, пусть и примитивный!
Аниматор напоминает оный из ранних версий Android: он имеет фиксированный набор свойств, которые умеет интерполировать в промежутках определенного времени. Если простыми словами: то он оперирует линейными отрезками времени a и b, в промежутке которых мы имеем значение «прогресса» — которое даёт нам результат от 0.0f (начало анимации) до 1.0f (конец анимации). Пока время тикает до необходимого интервала (duration), аниматор интерполирует заранее назначенные ему поля до нужных значений.
Именно так и получается плавность! Похожим образом реализованы анимационные системы во многих играх и мобильных ОС, только там они гораздо более комплексны: есть сериализация/десериализация из файлов, поддержка кейфреймов (несколько последовательных состояний на одном промежутке времени), поддержка кастомных свойств и т. п.
❯ Модем
Как я уже говорил раннее, работа с модемом происходит посредством AT-команд. Лучше всего обрабатывать ввод-вывод модема из отдельного потока, поскольку он может отвечать довольно медленно и тормозить UI-поток основной программы, вызывая лаги. В SIM800 уже реализован весь GSM-стек, в том числе декодирование и вывод звука через встроенный усилитель с фильтром — остается только подключить динамики и микрофон от нашего телефона. Пока что я подсобрал аудиотракт на том, что было под рукой — микрофон от нерабочего смартфона и динамик от планшета, но для проверки этого хватает:
Важный нюанс: по умолчанию, tty-устройства в Linux работают по терминальному принципу — т. е. дробят транзакции по символу окончания строки (\n), имеют ограниченный буфер и т. д. Для нормальной работы в условиях модема — когда фактически длина ответа неизвестна, а в сам ответ могут «вклиниваться» Unsolicited-команды (своеобразные флаги о состоянии от модема, которые могут прийти в произвольное время — т. е. при входящем звонке, модем начнёт флудить RING в терминал), необходимо иметь возможность точно прочитать весь буфер до конца и парсить данные «по месту». Для этого используется raw-режим терминала:
Пытаемся позвонить с помощью метода Dial и видим, что всё работает! Это очень круто! А теперь, конечно же, самое время переходить к реализации того, чего вы ждали — пользовательского интерфейса!
❯ Главный экран
К выбору концепции для интерфейса, я поступил максимально просто — «слизал» дизайн первых версий iOS. Как по мне, это одни из самых красивых версий iOS вообще — все эти приятные градиенты и переливания. Конечно, я не так крут, как инженеры Apple, да и мощного UI-фреймворка у меня пока что нет, поэтому я приступил к реализации с «минимальным» функционалом.
Начал я с разделения главного экрана на модули и продумывания архитектуры основного «лаунчера». У нас есть статусбар, который рисуется поверх всех приложений, полка с приложениями — AppDrawer и сами экраны приложений, унаследованные от суперкласса CScreen.
На данный момент, отрисовка достаточно примитивная: сначала рисуются фоновые обои, затем, если нет никаких активных экранов — AppDrawer и в самом конце рисуется статусбар и всевозможные оверлеи.
Выглядит симпатичненько. Если я смогу поднять хардварный GLES, то это получится сделать в разы плавнее и шустрее — не хуже айфонов тех лет! Реализация самого статусбара примитивненькая, но вполне рабочая:
Кроме этого, я сразу же реализовал предварительный механизм приложений в системе — пока что они слинкованы статически с основным лаунчером. Для этого есть структура CAppDesc, которая содержит минимально-необходимую информацию для показа информации о приложении и фабрику для создания его основного экрана.
Обратите внимание на удобство примененного подхода Immediate GUI. Нам понадобился новый элемент интерфейса, который описывает кнопку номеронабирателя? Мы просто реализовываем ещё один метод, который берет за основу стандартную кнопку и дорисовывает к ней текст. Всё крайне просто и понятно, хотя на данный момент слишком захардкожено. :)
❯ Звоним!
Пришло время совершить первый звонок с нашей по настоящему кастомной прошивки. Набираем номерок и…
Да, всё работает и мы без проблем можем дозвониться :)
❯ Заключение
Конечно же, это далеко не весь функционал, необходимый любому современному смартфону. Здесь много чего еще нужно реализовать хотя бы для соответствия уровню бюджетных кнопочных телефонов: телефонную книгу, поддержку СМС/ММС, мультимедийный функционал с играми. Однако начало уже положено и самая необходимая часть модулей реализована. Этот проект очень занимательный для меня и я горд, что смог не на словах, а на деле показать вам, моим читателям, возможности моддинга совершенно NoName-устройств, без каких либо опознавательных знаков…
Моя задача заключается в том, чтобы показать вам возможности использования старых телефонов не только в потребительских, но и в гиковских DIY-сферах. Судите сами: огромный классный дисплей, емкостной тачскрин, готовый звук, камера — и всё это за каких-то пару сотен рублей. Главное показать людям, как всю эту мощь использовать в своих целях и делать совершенно новые устройства из существующих, а не выбрасывать их на помойку! Сейчас смартфоны, подобные Fly из этого поста стоят копейки, а портировать на них прошивку можно без каких-либо трудностей. Я очень надеюсь, что после этого поста читатели попытаются сделать что-то своё из старых смартфонов, благо свои наработки я выкладываю на GitHub!
(Анонс) В одном из недавних постов, я создавал объявление о поиске айфона на мобильной винде: довольно редкой китайской подделке, которая стоила ~половину цены оригинала. И девайс нашелся - пикабушник @BarbedFrog согласился выслать мне девайс, ещё и доставку оплатил. Смартфон я уже отдриставрировал (именно так, т.к ориг. запчасти на него не найти), но теперь он полностью юзабелен. Статья про девайс и его сравнение с яблочным оригиналом выйдет в середине-конце мая!
Про подобные мини-айфоны я знаю уже лет 5, но по адекватной цене нашел только сейчас - всего за 150 рублей с родной коробочкой! Этот девайс имеет дисплей ~2.8" с неплохим разрешением для такой диагонали - 480x800, его размеры соизмеримы с ладошкой среднестатистического человека, он работает на Android, оптимизированном для подобных размеров и у него отличная компоновка внутри: насколько я знаю, такие девайсы почти не греются, а обслуживать их весьма несложно. Будете ждать статью?
Выспаться, провести генеральную уборку, посмотреть все новые сериалы и позаниматься спортом. Потом расстроиться, что время прошло зря. Есть альтернатива: сесть за руль и махнуть в путешествие. Как минимум, его вы всегда будете вспоминать с улыбкой. Собрали несколько нестандартных маршрутов.