Ещё один способ получить нестандартные данные в iOS

от автора

Привет!

Я хочу показать, как, не используя приватных API ( = не используя приватных фреймворков/классов/функций) можно собирать разнообразные данные о использовании устройства.

Вот описание информации, которую можно получить:

  • Мощность сигнала сотовой сети: RSSI в дБм и относительную мощность в «палках»
  • Качество сигнала WiFi (0 — плохо, 4 — хорошо)
  • Состояние регистрации в сотовой сети: наличие SIM, поиск сети
  • Тип сети передачи данных: 2G, 3G, WiFi
  • Заряд аккумулятора с точностью до процента (стандартные средства дают точность в 5%)
  • Включен ли «режим самолёта» («airplane mode»)
  • Включены ли различные сервисы: будильник, Airplay, VPN, перенаправление звонков, Nike+

Собственно, суть метода:

	typedef struct { 		BOOL itemIsEnabled[24]; 		char timeString[64];				 		int gsmSignalStrengthRaw;			 		int gsmSignalStrengthBars;			 		char serviceString[100];			 		char serviceCrossfadeString[100]; 		char serviceImages[2][100]; 		char operatorDirectory[1024];		 		unsigned serviceContentType; 		int wifiSignalStrengthRaw; 		int wifiSignalStrengthBars;			 		unsigned dataNetworkType;			 		int batteryCapacity;				 		unsigned batteryState;				 		char batteryDetailString[150];		 		int bluetoothBatteryCapacity; 		int thermalColor; 		unsigned thermalSunlightMode : 1; 		unsigned slowActivity : 1;			 		unsigned syncActivity : 1;			 		char activityDisplayId[256];		 		unsigned bluetoothConnected : 1; 		unsigned displayRawGSMSignal : 1;	 		unsigned displayRawWifiSignal : 1; 		unsigned locationIconType : 1; 	} iOS6Data;  	// retrieve data 	void *app = (__bridge void *)([UIApplication sharedApplication]); 	ptrdiff_t providerOffset = 52; 	void *provider = *(void**)(app + providerOffset); 	ptrdiff_t iOS6DataOffset = 116; 	iOS6Data *data = (iOS6Data*)(provider + iOS6DataOffset); 
Минимальная программа, демонстрирующая возможности подхода

enum { 	kTimeItem = 0, 	kLockItem, 	kAirplaneItem, 	kSignalStrengthItem, 	kServiceItem, 	kDataNetworkItem, 	kBatteryItem, 	kBatteryPercentItem, 	kNotChargingItem, 	kBluetoothBatteryItem, 	kBluetoothItem, 	kTTYItem, 	kAlarmItem, 	kPlusItem, 	kPlayItem, 	kLocationItem, 	kRotationLockItem, 	kDoubleHeightItem, 	kAirPlayItem, 	kVPNItem, 	kCallForwardItem, 	kActivityItem, 	kThermalColorItem  };  typedef struct { 	BOOL itemIsEnabled[24]; 	char timeString[64];				 	int gsmSignalStrengthRaw;			 	int gsmSignalStrengthBars;			 	char serviceString[100];			 	char serviceCrossfadeString[100]; 	char serviceImages[2][100]; 	char operatorDirectory[1024];		 	unsigned serviceContentType; 	int wifiSignalStrengthRaw; 	int wifiSignalStrengthBars;			 	unsigned dataNetworkType;			 	int batteryCapacity;				 	unsigned batteryState;				 	char batteryDetailString[150];		 	int bluetoothBatteryCapacity; 	int thermalColor; 	unsigned thermalSunlightMode : 1; 	unsigned slowActivity : 1;			 	unsigned syncActivity : 1;			 	char activityDisplayId[256];		 	unsigned bluetoothConnected : 1; 	unsigned displayRawGSMSignal : 1;	 	unsigned displayRawWifiSignal : 1; 	unsigned locationIconType : 1; } iOS6Data;  void proof_of_concept() { 	// we need to check runtime before start 	NSString *systemVersion = [[UIDevice currentDevice] systemVersion] ; 	NSScanner *scanner = [NSScanner scannerWithString:systemVersion]; 	int runtime; 	[scanner scanInt:&runtime]; 	 	if (runtime != 6) { 		NSLog(@"К сожалению, программа работает только на iOS 6"); 		 		UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Не удалось получить данные" 															message:@"Для работы программы необходима iOS 6." 														   delegate:nil 												  cancelButtonTitle:@"Закрыть" 												  otherButtonTitles:nil]; 		[alertView show]; 		 		return; 	} 	 	// retrieve data 	void *app = (__bridge void *)([UIApplication sharedApplication]); 	ptrdiff_t providerOffset = 52; 	void *provider = *(void**)(app + providerOffset); 	ptrdiff_t iOS6DataOffset = 116; 	iOS6Data *data = (iOS6Data*)(provider + iOS6DataOffset); 	 	// usage example 	NSMutableString *example = [NSMutableString stringWithCapacity:1000]; 	 	[example appendFormat: @"Сигнал сотовой сети: %d дБм\n", data->gsmSignalStrengthRaw ]; 	[example appendFormat: @"Заряд батареи: %@\n", @(data->batteryDetailString)];  	switch (data->dataNetworkType) { 		case 2: 			[example appendString: @"Тип сети передачи данных: 2G\n"]; 			break; 		case 3: 			[example appendString: @"Тип сети передачи данных: 3G\n"]; 			break; 		case 5: 			[example appendString: @"Тип сети передачи данных: WiFi\n"];  		default: 			break; 	} 	 	if (data->itemIsEnabled[kAlarmItem]) { 		[example appendString:@"Будильник включен"]; 	}  	if (data->itemIsEnabled[kCallForwardItem]) { 		[example appendString:@"Влючено перенаправление звонков"]; 	} 	 	if (data->itemIsEnabled[kAirplaneItem]) { 		[example appendString:@"Влючен \"Режим самолёта\""]; 	}  	 	NSLog(@"%@", example); 	 	UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Info" 													   message:example 													  delegate:nil 											 cancelButtonTitle:@"Закрыть" 											 otherButtonTitles: nil]; 	[alertView show]; } 

Плюсы:

  • Данные постоянно остаются актуальными, их обновлением занимается само приложение (UIApplication)
  • Решение не использует приватные API

Минусы:

  • Решение построено на рантайме iOS, поэтому структуры и константы отличаются для iOS 5, 6 и 7
  • Полученная информация довольна поверхностна, нельзя например получить другие статистики сотовой сети

Если интересно, как это работает, откуда берутся константы и описания структур данных, пишите в комментариях, дополню статью.

ссылка на оригинал статьи http://habrahabr.ru/post/183650/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *