2011-11-23

Harmattan. Пишем в Event Feed - часть 2 (детективная)

Пост посвящен погружению в недра EventFeed и является отчасти детективным. Советую читать не по диагонали, а подряд. А то будет как с Лорой Палмер. Вообщем, я предупредил. Поехали!

При разработке Calendar Feed я столкнулся со следующими фичами, которые хотелось бы реализовать:
  • Обновление элемента с помощью кнопки Refresh
  • Открытие календаря при нажатии на элемент EventFeed (желательно с переходом на конкретный день)
  • Обновление без необходимости держать подключение к интернету
Начнем пожалуй с конца.

Обновление без необходимости держать подключение к интернету
Как мы помним, документация утверждает что подключение необходимо и что это вот такой вот limitation. Ну где наша не пропадала, авось обойдем. Решение оказалось простым. В файле /etc/sync/profiles/service/calendarfeed.xml, как мы помним из предыдущего поста, был некий ключ destinationtype=online. В тот раз я его оставил как есть, как недокументированный никак и работающий. В этот раз я решил попробовать его поменять на offline. И вы знаете, чудо произошло, даже дунуть не пришлось. Теперь интернет приложению для обновления не нужен.

Открытие календаря при нажатии на элемент EventFeed
Так, что у нас есть в методе addItem у MEventFeed? Есть Url для открытия, но календарь не умеет открываться по урлу. Только через dbus (методом com.nokia.Calendar / com.nokia.maemo.meegotouch.CalendarInterface.showMonthView). Есть конечно вариант сделать приложение-прослойку, которое будет слушать урл и открывать календарь, но это не наши методы. Будем копать. 

Первая остановка - sqlite-база /home/user/.config/meegotouchhome-nokia/eventsfeed.data. В ней есть три таблицы, пока нас интересует только одна - events. В ней хранятся все, в текущий момент времени отображающиеся в UI, элементы EventFeed. В этой таблице есть один очень интересный атрибут - action. И у элементов Twitter'а там лежит вот такая строка:
com.nokia.twitter.eventfeed /EventFeedMessage com.nokia.twitter.eventfeed showItem AAAACgAAAAAkADEAMwA4ADUAMwA3ADMAMAA4ADYAMgAxADIANAAyADMANgA4

"Ага, dbus!" - сказали суровые сибирские мужики и пошли пробовать задать свой action (пока ручками в базе). Строка для вызова метода без параметров проходит, календарь открывается. Но нас интересует метод, у которого есть 3 параметра (год, месяц, число, которые показать в открывшемся календаре). Этот метод упорно не работает. Ладно, оставим это на потом.

Пока разберемся как же добавить action программно и желательно не через sqlite. Может нам опять поможет dbus? И верно! Есть метод com.nokia.home.EventFeed /eventfeed com.nokia.home.EventFeed.addItem, который принимает QVariantMap. И этот метод даже успешно скушал переданный ему QVariantMap с одноименными атрибутам таблицы параметрами. Уже легче. Как минимум, мы можем открыть календарь.

Обновление элемента с помощью кнопки Refresh
Тут поинтереснее. В базе есть таблица refreshactions с одним атрибутом action. Методом тыка было установлено, что она используется приложением Feeds для своих целей. И там, так же как и events, лежит строка с вызовом dbus-метода. Ага, а в списке методов com.nokia.home.EventFeed есть метод addRefreshAction. Он то нам и нужен.

Но! Как же обновить наш плагин? Неужели, все же придется писать свой демон, который будет ловить dbus и что-нибудь делать? К счастью, оказалось что это лишнее. Как мы помним из предыдущей статьи, мы добавляли в postinst и в prerm вызовы dbus для установки/удаления плагина. Может там есть что-нибудь и для обновления? И верно, есть. Метод com.meego.msyncd /synchronizer com.meego.msyncd.startSync. Который принимает строку - идентификатор плагина (а вернее профиля, который будет обновляться). В нашем случае - calendarfeed. Но просто так добавленный вызов dbus-метода не возымел никакого эффекта. Даже после ребута девайса.

Параметры dbus-методов в Sqlite-базе EventFeed
Все же параметры то было бы неплохо передать, не правда ли? Ведь гораздо приятнее, когда календарь открывается на дате первого события из списка, нежели на последней открытой дате, да и для рефреша тоже нужно. 

Причем Twitter-то хорошо воспринимает параметры. Может дело в том, что у Twitter'а параметр строчный, а у нас целочисленные? Нет, не то. Строчные тоже не работают (проверено на примере с RefreshAction). Стоп. А если запустить вызов метода Twitter'а из консоли? Показывает Tweet Not Found. Хммм. Видимо на параметры накладывается преобразование. И верно, уж очень длинная строка с идентификатором для twitter'а. И на что-то подозрительно похожая. 

Точно! Это же base64. Быстрая проверка показала что, да, это base64. Он в себе содержит следующее:


00000000  00 00 00 0a 00 00 00 00  24 00 31 00 33 00 38 00  |........$.1.3.8.|
00000010  35 00 33 00 37 00 33 00  30 00 38 00 36 00 32 00  |5.3.7.3.0.8.6.2.|
00000020  31 00 32 00 34 00 32 00  33 00 36 00 38           |1.2.4.2.3.6.8|

Любопытно. Мы видим нормальный для Twitter'а идшник 138537308621242368, записанный в UTF-16 строку и какие-то бинарные данные перед этим. Хм. 0x24. Это 36. Строка получается как раз 36 байт. Видимо длина. Попробуем заменить данные на calendarfeed и поставить соответствующую длину. После этого взять base64 от получившегося файла и отправить в refreshactions. Ух ты, работает. Кнопка Refresh обновила мой элемент!

Осталось понять одно - как генерить такие данные более удобным способом, нежели записывание байт и что за 0x0a в начале.

И вообще что-то это подозрительно напоминает. Некий идентификатор (0x0a), размер, данные. Хм. И все это работает через Qt (strings для бинарника, который этим всем занимается (/usr/bin/meegotouchhome), выдал огромную кучу используемых библиотек и методов из Qt).

Эврика! Это же QVariant, сериализованный в QDataStream. Быстрая проверка показала, что я не ошибся. Проверим для целочисленных параметров в календаре... Работает! Ура, мы победили!

P.S.
За помощь в написании этого поста хочу сказать спасибо:
hexdump, strings, dbus-monitor, qdbus, grep и конечно же sqlite3.
Без них не было бы ни этого поста, ни версии 0.2.4 приложения Calendar Feed (в исходниках которого можно посмотреть практическое применение выкладок выше).

4 комментария:

  1. Очень круто и весело расписал реверс-инжинириг. Хакинг - это весело :-) Так держать.

    ОтветитьУдалить
  2. Этот комментарий был удален автором.

    ОтветитьУдалить
  3. Только сейчас набрел на статью.. действительно весло =)

    Насчет параметров dbus-метода addRefreshAction - очень похоже на то что делает MRemoteAction::toString.

    Пример видел здесь:
    http://harmattan-dev.nokia.com/docs/library/html/guide/html/Developer_Library_Developing_for_Harmattan_Integrating_event_feed_into_applications_Example_of_updating_event_feed_with_a_D-Bus_call.html

    ОтветитьУдалить