Из этой статьи ты узнаешь, как можно общаться с iPhone, iPad и iPod touch из своих программ под Mac OS X, не прибегая к скриптингу iTunes. В результате сегодняшней работы мы получим полный доступ к файловой системе мобильного устройства с помощью закрытого фреймворка Mobile Device Framework.
Введение
Мне нередко приходилось видеть в сети вопросы типа: «Можно ли использовать мой iPhone как флешку?». Действительно, памяти на устройстве установлено немало, хотелось бы иметь возможность использовать ее свободно, а не только для хранения файлов, полученных через iTunes в различных сервисах Apple. Однако iPhone’ы и iPod’ы (имеются в виду iPod touch) при подключении не монтируются как съемные носители ни в Mac OS X, ни в виндах.
Apple действительно не предоставила такой функциональности в своих устройствах, но общественность не дремлет, и со временем появилось множество утилит, которые дают пользователю возможность работать с устройством Apple как со съемным носителем. Некоторые из них устанавливаются на iPhone, некоторые — на хост.
Одни работают через Wi-Fi (например FlashDrive), другие — через USB (iPhone Folders). Нас будет интересовать в основном работа через USB – Wi-Fi не везде есть, да и скорость обмена данными в этом случае оставляет желать лучшего.
Готовые тулзы
Их немало. Те, что работают через USB, можно условно разделить на два класса: одни нужно ставить на мобильное устройство (тогда потребуется и jailbreak), другие следует устанавливать на хост.
В качестве примера последних можно привести iPhone folders (iphonefolders.com). iPhone Folders — это расширение Windows Explorer, которое позволяет просматривать, копировать и удалять содержимое iPod touch или iPhone, соединенного с компьютером через USB, как в обычной папке проводника. В общем, у аппарата появляется функциональность обычного съемного диска, при этом jailbreak не требуется, но на хосте должен быть установлен iTunes.
Таких поделок существует достаточно много (Touch Drive, Touch Copy и тому подобные), а некоторые из них даже стоят денег, хотя, как мы потом увидим, ничего сложного в создании таких прог нет.
В качестве аналогичного решения для Mac OS X можно привести iPhoneDisk — плагин к MacFuse, который также позволяет работать с файловой системой iPhone.
Среди прог, которые устанавливаются на само мобильное устройство и обеспечивают его монтирование при подключении как съемного носителя, можно отметить, например, USB Drive, которая доступна через Cydia. Подобные приложения имеют существенные недостатки, потому что они, как правило, предлагают на выбор три режима работы, для переключения между которыми требуется перезагрузка мобильного устройства. В режиме Default устройство будет работать как обычно: гаджет можно будет использовать в качестве модема, распознавать его в iPhoto как фотокамеру (по протоколу PTP — Picture Transfer Protocol) и синхронизировать с iTunes.
Второй режим, Drive + iTunes, работает только в Mac OS X. В этом случае интерфейс PTP будет заменен другим, Mass Storage, что позволит использовать девайс в качестве USB-флешки. При этом синхронизация с iTunes и дебаггер XCode продолжат работать как в дефолтном режиме. А в режиме Drive Only устройство определится только как USB-диск. При этом он будет виден в любой операционной системе.
Протоколы iTunes
Все тайное рано или поздно становится явным, и как бы Apple не пыталась закрыть протоколы, по которым ее мобильные устройства взаимодействуют с iTunes, всегда найдутся ребята, которые все это прогонят под USB-снифером, отреверсят софт, драйвера и проведут все остальные необходимые операции. Так как Apple традиционно не поддерживает Linuх, обеспечивать работу ее устройств в этой операционке пришлось сообществу. Так появился проект libimobiledevice (libimobiledevice.org). libimobiledevice — это библиотека, которая обеспечивает взаимодействие с iPhone, iPod touch, iPad и Apple TV. В отличие от других подобных проектов, она не зависит от стороннего проприетарного программного обеспечения и не требует jailbreak’а устройств. Эта библиотека позволяет другим приложениям свободно работать с данными на мобильных устройствах, делать бэкапы, управлять иконками SpringBoard, управлять установленными на устройствах приложениями, синхронизировать музыку и видео.
На рисунке видно, что библиотека взаимодействует с USBустройствами через libusb-1.0. usbmuxd — демон, который обеспечивает мультиплексную передачу данных по TCP/IP через USBинтерфейс. Таким образом, для остального софта USB-интерфейс становится прозрачным и он может взаимодействовать с сервисами, бегающими на мобильном устройстве, через сокеты. Для обмена данными с демоном используется библиотека libusbmuxd. libiphone — это уже реализация протоколов сервисов iOS. Если приложение работает с файловой системой мобильного устройства, то оно использует AFC- (или AFC2-) протокол. AFC (Apple File Connection) — это сервис, который доступен на каждом iPhone/iPod touch. Именно этот сервис iTunes использует для обмена файлами с устройством.
Собственно libmobiledevice для работы с файловой системой iPhone из Mac OS X нам не понадобится: можно было бы заюзать связку usbmuxd/libiphone, но мы пойдем по другому пути и воспользуемся побочным продуктом разработки libmobiledevice — результатами реверсинга MobileDevice.framework. Этот закрытый фреймворк идет вместе с Mac OS X и предоставляет высокоуровневый интерфейс к мобильному устройству. Найти его можно по пути /System/Library/PrivateFrameworks/MobileDevice.framework. Хедеры там, понятное дело, найти невозможно. А вот найти хедеры, которые появились после исследования этого фреймворка реверс-инженерами, можно на theiphonewiki.com. Как видно по найденому нами mobiledevice.h, функции в MobileDevice.framework работают на достаточно высоком уровне, так что заморачиваться с USB-мультиплексингом и прочим нам не придется. В качестве примера работы с MobileDevice. framework мы создадим класс для обмена файлами с устройством.
Кодим
Кодить будем на Objective-C/Cocoa. Не забудь в проекте XCode добавить ссылку на MobileDevice.framework, чтобы все нормально слинковалось. Создадим два класса: MobileDevice и MobileDeviceServer.
Первый будет отвечать за работу с файловой системой мобильного устройства, второй — за подключение/отключение устройств.
Интерфейс наших классов
#import <Cocoa/Cocoa.h>
#import "MobileDevice.h"
@interface MobileDevice : NSObject
{
@public struct am_device * dev;
struct afc_connection * conn;
}
- (MobileDevice *) initWithDevice: (struct am_device *) device;
- (MobileDevice *) copy;
// В этом методе инициируем AFC соединение
- (BOOL) connect;
// Получаем свойства устройства, например тип или имя
- (NSString *) getValue: (CFStringRef) name;
// Собственно работа с файловой системой
- (BOOL) pathExist: (NSString *) path;
- (BOOL) downloadFile: (NSString *) remote_path toLocation: (NSString *) local_path;
- (BOOL) uploadFile: (NSString *) local_path toLocation: (NSString *) remote_path;
- (BOOL) downloadDirectory: (NSString *) remote_path toLocation: (NSString *) local_path;
- (BOOL) uploadDirectory: (NSString *) local_path toLocation: (NSString *) remote_path;
- (BOOL) removeDirectory: (NSString *) remote_path;
- (BOOL) isDirectory: (NSString *) path;
@end
// Сервер сделаем синглтоном, поэтому методов
// init у него не будет, а defaultServer возвращает
// единственный инстанс класса
@interface MobileDeviceServer : NSObject {
@public
NSMutableArray * MobileDevices;
}
+ (MobileDeviceServer *) delfaultServer;
@end
Реализация MobileDeviceServer простая, поэтому приведу ее здесь полностью. Основная задача этого класса — получать уведомления от MobileDevice.framework о подключении/отключении устройств и формировать список подключенных, что бы потом хендлер устройства можно было использовать при создании инстанса класса MobileDevice.
@implementation MobileDeviceServer
static MobileDeviceServer * DefaultServer = nil;
static void AmDeviceNotificationCallback(struct am_device_notification_callback_info * info)
{
if (info->msg == ADNCI_MSG_CONNECTED)
{ // Новое устройство
MobileDevice * device = [[MobileDevice alloc] initWithDevice: info->dev];
[device connect];
[DefaultServer->MobileDevices addObject: device];
}
else if (info->msg == ADNCI_MSG_DISCONNECTED)
{ // Отключили устройство
for (int i = 0; i < [DefaultServer->MobileDevices count]; ++i)
{ // Ищем его в своем списке и удаляем
if (((MobileDevice *)[DefaultServer->MobileDevices objectAtIndex: i])->dev == info->dev)
{
[DefaultServer->MobileDevices removeObjectAtIndex: i];
break;
}
}
}
}
+ (MobileDeviceServer *) delfaultServer
{
if (DefaultServer == nil)
{
DefaultServer = [[MobileDeviceServer alloc] init];
// Тут будем хранить список подключенных устройств
DefaultServer->MobileDevices = [[NSMutableArray alloc] init];
// Подпишемся к уведомлениям от MobileDevice.framework
struct am_device_notification * subscription;
if (AMDeviceNotificationSubscribe(&AmDeviceNotificationCallback, 0,0,0,&subscription) != 0)
{ // Не получилось :(
[DefaultServer->MobileDevices release];
[DefaultServer release];
DefaultServer = nil;
}
}
return DefaultServer;
}
@end
В качестве примера работы с девайсом через AFC приведу реализацию метода downloadFile класса MobileDevice. Всю реализацию приводить здесь смысла нет, но ты можешь найти ее на диске.
- (BOOL) downloadFile: (NSString *) remote_path toLocation: (NSString *) local_path
{
if (conn == nil) return FALSE;
afc_file_ref file_ref;
if (AFCFileRefOpen(conn, [remote_path cString], AFC_MODE_READ, 0, &file_ref) != 0)
return FALSE;
FILE * local_file = fopen([local_path cString], "w");
if (local_file == NULL)
{
AFCFileRefClose(conn, file_ref);
return NO;
}
char buffer[10000];
int len;
do
{
len = sizeof(buffer);
if (AFCFileRefRead(conn, file_ref, buffer, &len) != 0)
{
fclose(local_file);
AFCFileRefClose(conn, file_ref);
return NO;
}
fwrite(buffer, len, 1, local_file);
} while(len == sizeof(buffer));
fclose(local_file);
AFCFileRefClose(conn, file_ref);
return YES;
}
Заключение
Теперь, зная, как из Mac OS X получить доступ к файловой системе мобильных устройств Apple, для тебя несложно будет написать подобие iPhone folders и других альтернативных утилит. Дополнительным плюсом работы с iPod/iPhone из Mac OS X является то, что нет необходимости устанавливать стороннее программное обеспечение, как в виндах (iTunes, Apple mobile device support и так далее). Все будет работать на голой системе с диска.