понедельник, 20 мая 2013 г.

Немного о взаимодействии приложений в Tizen

Исходный код, описанных здесь примеров можно найти на Github

Нередко возникают задачи, когда вашему приложению необходимо использовать функционал сторонней программы или наоборот, оно может выполнять некую обработку, необходимую другим приложениям. Примерами таких взаимодействий могут быть: использование стандартного медиа прогрывателя для воспроизведения видео или аудио, перевод текста, если в системе установленно приложение для этих целей, отправка смс сообщения  и т.п. В этой заметке я постараюсь немного описать модель таких взаимодействий под платформой Tizen.
Рассмотрим 2 приложения TizenSender и TizenReceiver. Первое будет отправлять текст, а второе, соответственно, получать, обрабатывать и возвращать ответ Sender'у. Для обмена подобными сообщениями, приложение, которое посылает информацию, в нашем случае это TizenSender должно иметь привелегию http://tizen.org/privilege/application.launch в manifest.xml файле. В свою очередь, приложение, которое экспортирует возможности для других, должно содержать в своем манифесте AppControl с идентификатором операции, которую оно выполняет. Этот идентификатор имеет формат web uri сохраная при этом даже префикс http. Не понятно для чего было изменять стандарты принятые, например, в android, но разработчикам Tizen видней. В нашем случае мы зададим operationId, как http://example.org/tizen/communication.
В целом схема взаимодействия выглядит примерно так:
Начнем разработку примера с TizenSender'а, отталкиваясь от схемы выше. Для данных целей, нас интересуют 2 метода из класса AppManager из Tizen SDK это:
static AppControl* FindAppControlN (const AppId &appId, 
                   const Tizen::Base::String &operationId)
static Tizen::Base::Collection::IList* FindAppControlsN (const Tizen::Base::String *pOperationId, 
                   const Tizen::Base::String *pCategory, 
                   const Tizen::Base::String *pDataType, 
                   const Tizen::Base::String *pUriPattern)
Или один из AppControl:
static result FindAndStart(const Tizen::Base::String &operationId,
              const Tizen::Base::String *pUriPattern, 
              const Tizen::Base::String *pDataType, 
              const Tizen::Base::String *pCategory, 
              const Tizen::Base::Collection::IMap *pExtraData, 
              IAppControlResponseListener *pListener)
Первый метод принимает во входные параметры appId, и operationId. Т.к. appId уникальный, вы можете быть уверены что вам вернется AppControl нужного приложения. Второй метод пытается подобрать исполнителя только по operationId, и соответственно возвращает коллекцию всех возможных вариантов. Ну и последний метод, по сути своей, это совокупность метода номер 2 плюс метод Start. Он также попытается найти обработчик по operationId, но выбор подходящего приложения предоставит пользователю. Аргументы передаются через коллекцию ключ-значение extraData. Для стандартных контролов необходимые элементы коллекции предопределены. Для ваших собственных можно использовать любые произвольные, главное чтобы они были согласованы.
Пользовательский интерфейс будет простым, на 2 действия. Найти список приложений готовых обработать данный AppControl. Нужен чтобы убедиться, что TizenReceiver установлен и готов к работе. И второе, отправить введенное пользователем сообщение.
Далее переходим к TizenReceiver. Как уже указано выше, первое о чем стоит позаботиться, это добавить наш operationId к AppControl'ам в файле манифеста. 
Теперь необходимо зарегистрировать объект реализующий интерфейc IAppControlProviderEventListener:
void TizenReceiverApp::setAppControlListener(Tizen::App::IAppControlProviderEventListener* listener) {
 using namespace Tizen::App;

 AppControlProviderManager* mgr = AppControlProviderManager::GetInstance();
 AppAssert(mgr);

 result r = mgr->SetAppControlProviderEventListener(listener);
 TryReturnVoid(!IsFailed(r), "Setting AppControlProviderEventListener failed. Error: %s",
   GetErrorMessage(r));
 AppLog("AppControlProviderEventListener was registered successfully");
}
Здесь есть небольшой нюанс. Метод SetAppControlProviderEventListener должен быть вызван до того, как сработает OnAppControlRequestReceived, точнее в App::OnAppInitializing. Иначе запрос будет просто проигнорирован. При этом нужно учесть, что после того как приложение будет инициализировано и UI контролы будут созданны указатель на коллекцию параметров extraData уже будет недействителен.
Возвращение результата, происходит аналогично, через коллекцию типа ключ-значение, с помощью метода OnAppControlRequestReceived класса OnAppControlRequestReceived. В итоге получаем:
Принцип работы прост. Запускаем TizenSender, пишем какой-либо текст в поле Message и жмем Send text. Должно открыться окно TizenReciver, в котором будет видно пришедший, и слегка измененный текст, который будет ответом. После нажатия Send response мы должны снова вернуться в окно TizenSender. Это все, остальные подробности нужно искать в документации.

суббота, 27 апреля 2013 г.

Tizen. Чиним сеть в эмуляторе на Ubuntu 12.10 и 13.04

Установка Tizen SDK проходит без каких-либо дополнительных телодвижений, и в целом вопросов не возникает. Однако если вы хотите получить приемлемую скорость работы эмулятора, необходимо настроить работу kvm в вашей системе. Для этого можно воспольpзоваться руководством с ubuntu help. Именно так я и поступил, и в итоге получил работающий эмулятор с неработающей сетью. Дальше о том, из-за чего это произашло и как все это починить.
В документации по эмулятору есть пункт посвященный работе сети. Если коротко это выглядит так:  
Теперь необходимо убедиться что в нашей системе все работает, как задумано. Для начала проверим настройки сети внутри гостевой системы. Быстрей всего это можно сделать через командную оболочку. Для этого достаточно нажать правой кнопкой на главном окне Tizen эмулятора и выбрать пункт Shell. Дальше мы получаем самый обычный linux терминал с правами root, поэтому можем посмотреть состояние сети через
    sh-4.1# ifconfig eth0
    eth0      Link encap:Ethernet  HWaddr BC:AE:C5:8F:80:A3  
          inet addr:10.0.2.16  Bcast:10.0.2.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:273877 errors:0 dropped:0 overruns:0 frame:0
          TX packets:210089 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:26920315 (25.6 MiB)  TX bytes:16432995 (15.6 MiB)
    sh-4.1# route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use     Iface
    0.0.0.0         10.0.2.2        0.0.0.0         UG    0      0        0 eth0
    10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
    10.0.2.2        0.0.0.0         255.255.255.255 UH    0      0        0 eth0
Видим  что это вполне согласуется со стандартным положением из документации Tizen OS, эмулятор имеет адресс 10.0.2.16 и в качестве маршрута по умолчанию использует 10.0.2.2. Теперь необходимо убедиться что настройки хоста соответствуют данным требованиям. Для этого в его терминале
    $> sudo ifconfig 
    virbr0    Link encap:Ethernet  HWaddr 5e:9e:72:a5:d8:d4  
          inet addr:192.168.122.1  Bcast:192.168.122.255  Mask:255.255.255.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
    ...
    $> sudo iptables -t nat -L -n
    Chain PREROUTING (policy ACCEPT)
    target     prot opt source               destination         
    
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination         
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination         
    
    Chain POSTROUTING (policy ACCEPT)
    target     prot opt source               destination         
    MASQUERADE  tcp  --  192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
    MASQUERADE  udp  --  192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
    MASQUERADE  all  --  192.168.122.0/24    !192.168.122.0/24  
Видно, что нужный интерфайс хоста указывает не на тот адресс, и NAT работает не на ту подсеть соответственно. Решить данную проблемму можно воспользовавшись интерфейсом virsh
    $>sudo virsh    
    virsh # net-destroy default
    virsh # net-edit default    
В появившемся окне редактора, нужно заменить значения ip адресса интерфейса и пула адрессов.
      1 
      2   default
      3   b202ae10-db4a-c6ff-9988-4a2a197f92ba
      4   
      5   
      6   
      7     
      8       
      9     
     10   
     11 
Например так
      6   
      7     
      8       
      9     
И переконфигурировать интерфейс
    virsh # net-start default
    Network default started

Еще раз через iptables и ifconfig проверяем, что все параметры установленны верно. Можно убедиться в работе из гостевой ОС ping 10.0.2.2.
Однако, остается еще одна проблемма, при данных настройках, в эмуляторе не определен dns сервер. Поэтому пинг до того же google.com не проходит. Это можно решить используя свой resolv.conf. В консоли хостовой машины:
     $> echo "nameserver 8.8.8.8" > resolv.conf
     $> ~/tizen-sdk/tools/sdb -e push resolv.conf /etc/resolv.conf
    resolv.conf                     0 KB/s (19 bytes in 0.049s)
     $> rm resolv.conf 
Все. Проблемма исправлена, можно разрабатывать сетевые приложения :). PROFIT!