Категория: Программирование

SMS-сендер для Android: Исследуем недра операционной системы с помощью дебаггера и не только

Когда OS Android только появилась, многие, и я в том числе, мечтали, чтобы на нее как можно скорее портировали Qt. К сожалению, корпорация добра не оправдала наших надежд, сообщив, что SDK Андроида будет только на Java. Новость о покупке Trolltech корпорацией Nokia тоже не добавила оптимизма.

Спустя некоторое время к нам привалила нежданная радость — для Андроида вышел NDK для нативной разработки на C++, и, конечно же, нашлись люди, которые стали портировать Qt на Android. На данный момент порт уже более-менее юзабелен — работают (и почти не глючат) практически все модули. Ну что ж, посмотрим, какие возможности открывает нам этот порт.

Как оно работает?

Поначалу кажется, что данный порт — это очень большой костыль. Без Java все равно не обошлось — с помощью NDK нельзя создавать исполняемые файлы, можно только библиотеки .so. На Java, по сути, нужно написать всего одну строчку, которая загружает нашу библиотеку на Qt. Далее виртуальная машина Android запускает Java-приложение, которое, в свою очередь, грузит нашу либу.

Сборка QT

Весь процесс очень хорошо описан в Wiki проекта (см. ссылки), но он содержит несколько граблей, поэтому кое-какие пояснения нам дать все же придется. Небольшая оговорка — процесс описывается для Ubuntu 10.04, но на других дистрибутивах, в принципе, все должно происходить так же. А вот для того, чтобы провернуть это дело под виндой, тебе придется немного попрыгать с бубном (какая тонкая ирония, а?).

Итак, поехали. Создаем директорию для SDK. Пишем в консоль:

<code data-result="[object Object]" data-second_best="[object Object]"> wget android-lighthouse.googlecode.com/files/
qadk-1.x-2.x-rtti-exceptions.tar.lzma
tar xvfa qadk-1.x-2.x-rtti-exceptions.tar.lzma

Клонируем репозиторий Lighthouse:

<code data-result="[object Object]" data-second_best="[object Object]">gitclonegit://gitorious.org/~taipan/qt/androidlighthouse.git

Редактируем файл mkspecs/android-g++/qmake.conf. В нем нужно изменить NDK_ROOT и ANDROID_PLATFORM (у меня — /data/local/qt и android-5 соответственно). Эти параметры отвечают за расположение собранной библиотеки и ее версию. Также нужно отредактировать файл androidconfig.sh. Настоятельно рекомендую заменить shared на static (для статической сборки библиотеки и приложений). Все, конфигурируем (./androidconfig.sh) и собираем (make -j X, где X — количество ядер твоего процессора).

Все? Не тут-то было! Не знаю, как обстоят дела с другими дистрибутивами, но на Ubuntu «make» вылетал с ошибкой, говорящей о недоступности заголовочных файлов OpenGL. Чего только я не предпринимал… Поставил все, что можно было, но решение оказалось куда проще — надо было просто переустановить имеющиеся в системе заголовочные файлы OpenGL. После этого можно повторять команду make -j X и идти… нет, не пить пиво, а курить мануалы по разработке под Android — информация лишней не бывает никогда, а собираться оно будет долго .

Создание приложения

Запускай Qt Creator, создавай новое GUI-приложение. В нем (вернее, в файле .pro) нам нужно будет изменить несколько строчек. Они должны выглядеть так:

TEMPLATE = lib
CONFIG += dll

В настройках Qt Creator нужно также указать путь до нашего (андроидовского) qmake — у меня это /data/local/qt/bin/qmake. Вообще, я бы посоветовал сначала дебажить приложение как десктопное и только потом изменять параметры сборки.

Кстати, я ведь еще не говорил, что за приложение мы будем писать? Это будет приложение для отправки СМС на номера самых различных операторов. Это возможно благодаря сервису smste.ru, который мы и будем использовать.

Не буду вдаваться в подробности сниффинга, скажу только, что я использую для этих целей Wireshark. Разберем алгоритм отправки сообщения:

  1. Делаем GET рута — главной страницы сайта, выдираем оттуда нужные нам значения input’ов (те, которые hidden), а заодно и кукисы.
  2. Запрашиваем капчу по номеру телефона и показываем ее пользователю.
  3. Отправляем POST-запрос с сообщением.

Для отправки HTTP-запросов в Qt существует класс QHttp. Кстати, не забудь подключить модуль QtNetwork (QT += network) в файле проекта!

Набросай форму (мою ты можешь увидеть на скриншоте) и приступай к кодингу. От объекта http класса QHtpp нам требуются только два сигнала — done() и readyRead(). Сразу при создании главного виджета отправим GET-запрос главной страницы:

<code data-result="[object Object]" data-second_best="[object Object]"> http.setHost(«smste.ru»);
http.get("/";

Сигнал done(), по сути, и не используется — по нему можно будет только опознать ошибку сетевого уровня (например, отключение Wi-Fi). Рассмотрим некоторые части слота onHttpReadyRead(const QHttpResponseHeader &resp):

<code data-result="[object Object]" data-second_best="[object Object]"> QString str(http.readAll());
qint32 index=str.indexOf(«value=\»code"+7;
if ( index != 6 )
codeMod = str.mid(index,
str.indexOf("\" />", index) — index);

Здесь мы копируем «спрятанную» (hidden) переменную codeMod из исходника страницы. Идем дальше:

<code data-result="[object Object]" data-second_best="[object Object]"> QString cookieStr;
for ( qint8 i = 0;
i < resp.values().count(); i++ )
{
if ( resp.values().at(i).first ==
«Set-Cookie» )
cookieStr.append(
resp.values().at(i).second+'\n');
}
cookies = QNetworkCookie::parseCookies(
cookieStr.toAscii());

Ну, а в этом куске кода, как ты, наверное, догадался, мы парсим печеньки. cookies — это QList из QnetworkCookie.

<code data-result="[object Object]" data-second_best="[object Object]"> qint32 index =
str.indexOf("/pix/" + 12;
image = str.mid(
index,str.indexOf(".jpg" — index
);
QHttpRequestHeader header = createHeader(
«GET»,QString("/pix/%1.jpg".arg(image)
);
http.request(header);

Здесь копируется адрес капчи (запрос адреса я покажу чуть позже) и посылается запрос этого самого JPEG’а. А вот так он сохраняется:

<code data-result="[object Object]" data-second_best="[object Object]"> if ( resp.value(«Content-Type») == «image/jpeg») {
ui->captchaLb->setPixmap(QPixmap::
fromImage(QImage::fromData(
http.readAll())));
return;
}

Так, с этим слотом разобрались. Капчу нужно запрашивать, как только пользователь введет номер телефона, то есть, когда закончится редактирование текста ui->numberLE. Для этого есть специальный слот:

<code data-result="[object Object]" data-second_best="[object Object]"> void MainWidget::on_numberLE_editingFinished()
{
if ( ui->numberLE->text().length() != 11 )
return;
QHttpRequestHeader header = createHeader(
«GET»,
QString("/netxml.php?number=%1&rnd=94728".
arg(ui->numberLE->text()));
http.request(header);
}

Функцию createHeader() смотри на врезке — она создает хедер HTTP-запроса (вообще, можно и проще, но нам надо отправлять еще и куки). Остался последний слот — нажатие кнопки «Отправить», и он предельно прост:

<code data-result="[object Object]" data-second_best="[object Object]"> QHttpRequestHeader header = createHeader(«POST»,
"/";
header.addValue("", QString(«number=%1&
message=%2&sign=ax-soft.ru&event=%3&
codemod=%4&%5=%6»).arg(ui->numberLE->
text()).arg(ui->textPTE->toPlainText()).
arg(image).arg(codeMod).arg(codeMod).arg(
ui->captchaLE->text()));
qDebug() << header.toString();
http.request(header);

Вот и все! Делай Build All, собирай .apk-пакет .

Создание виртуальной машины

Для тестирования приложения нам нужно создать виртуальную машину. Кстати, надеюсь, у тебя установлена Java Runtime Environment?

Если нет, то поставь, вещь нужная. Кроме того, для создания .apkпакетов понадобится ant. Ставится он легко — sudo apt-get install ant. Теперь переходи в сабдиректорию tools в Android SDK и вводи ./ android. Запустится менеджер настроек и виртуальных машин.

Сначала скачай нужные API (разобраться нетрудно, для этого примера нужна версия 8), далее переходи на вкладку Virtual Devices, жми New. В Name — любое имя, Target — Android 2.2, Skin — какой хочешь (я выбрал WVGA800), и нажимай Create AVD.

Затем выбирай машину и жми Start, Launch. Все, будем ждать. На моем нетбуке оно запускалось около десяти минут, на десктопе — 1,5-2 минуты. Работает эмулятор так же медленно, как и запускается (ибо эмулирует ARM с помощью QEMU). С одной стороны это плохо, что все тормозит, а с другой стороны — мы получаем достоверные на 100% результаты. Как только появится рабочий стол Android, виртуальную машину можно будет оставить в покое.

Создание тестового проекта

Переходим в директорию tools Android SDK (в консоли). Открываем документацию, начинаем вкуривать. Вводим: ./android create project. Опс, ошибочка! Смотрим, чего от нас хотят. Ага, мы не указали параметры нашего будущего проекта, а точнее: цель. Нужна версия API, путь до проекта, его имя, имя Activity и имя пространства имен для приложения. У меня получилось вот так:

<code data-result="[object Object]" data-second_best="[object Object]"> ./android create project --target 8 --name hello --path ./TestPro --activity helloActivity --package com.example.hello

Делаем ls… ага, вот она — директория TestPro. Входим в нее, и опять вызываем ls. Далее в директории libs нужно создать сабдиректорию armeabi. В нее мы копируем нашу собранную Qt’шную либу (.so).

В каталог src/ надо скопировать все содержимое androidlighthouse/ src/android/java/com, чтобы получилось src/com/nokia/ qt. После этого идем в src/com/example/hello/ и редактируем там главный Activity — helloActivity.java. Удаляем onCreate, добавляем функцию:

<code data-result="[object Object]" data-second_best="[object Object]">public helloActivity()
{
setApplication(«Hello»);
}

Здесь Hello — имя приложения. Следовательно, наша библиотека .so должна называться libHello.so. Ну и, наконец, идем в консоли в корень проекта и командуем ant install. Ждем (долго, поскольку либа статическая и весит много. У меня, например — 12.5 Мб). После того, как в консоли появится заветное SUCCESSFUL, можно идти в главное меню Андроида и запускать оттуда свое приложение.

Заключение

Когда-то (то ли в 2007, то ли в 2008) у меня на телефоне (Motorola A1200e, один из первых телефонов с Linux, и, кстати, с гуем, написанным на Qt 2) появилась QTopia, также известная как Qt Embedded — встраиваемая ОС от Trolltech на базе Linux Kernel 2.6 с оболочкой на Qt 4, заброшенная после покупки троллей нокией.

Появилась она благодаря труженикам с форума motofan, сумевшим портировать ее на ядро 2.4 (другого у A1200 не было и не будет, поэтому не будет и Андроида). Так вот, когда я ее поставил, был удивлен простотой портирования приложений с десктопа на телефон — иногда требовалось просто пересобрать его кросскомпилятором, и все! К сожалению, новомодного Qt 4.5 платформа не получила (и зря — на мой взгляд, она была не хуже, чем Maemo). Теперь такой метод портирования возможен и на Android, а ведь за ним будущее. И, кстати, вовсю идет портирование Qt Mobility, классного фреймворка для телефонов Nokia. Жаль, пока что портированием занимается только один, пусть и очень крутой человек (кстати, помочь не же лаешь?). В общем, нам осталось дождаться портирования Qt на iOS (там, к сожалению, все далеко не так радужно), и тогда можно будет смело заявлять, что лозунг Qt Software не высосан из пальца.

THANKS TO

Огромное спасибо румыну taipanromania (автор порта) и marflon (раньше, кстати, писал в ][) за помощь с созданием .apk, ну и, традиционно, группе И-3-1 (Прикладная Математика) МГТУ «Станкин».

Создание HTTP-заголовка

<code data-result="[object Object]" data-second_best="[object Object]">QHttpRequestHeader MainWidget::createHeader(
const QString &method,
const QString &path
)
{
QHttpRequestHeader header(method, path);
header.addValue(«Host», «smste.ru»);
header.addValue(«Connection», «keep-alive»);
header.addValue(«User-Agent», «Mozilla/5.0»);
header.addValue(«Referer», «smste.ru»);
header.addValue(«Accept», "*/*";
QString cookie;
for ( qint8 i = 0; i < cookies.length(); i++ )
cookie += ( cookies.at(i).toRawForm(
QNetworkCookie::NameAndValueOnly) + "; ";
header.addValue(«Cookie», cookie);
return header;
}

Links

Обсудить у себя 0
Комментарии (0)
Чтобы комментировать надо зарегистрироваться или если вы уже регистрировались войти в свой аккаунт.

Войти через социальные сети:

vitminakov
vitminakov
Было на сайте никогда
22 года (21.11.1994)
icq: 923695
Читателей: 18 Опыт: 0 Карма: 1
Я в клубах
Любители книг Пользователь клуба
все 16 Мои друзья