Привет!
Я хочу показать, как, не используя приватных 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: @"Тип сети передачи данных: 2Gn"];
break;
case 3:
[example appendString: @"Тип сети передачи данных: 3Gn"];
break;
case 5:
[example appendString: @"Тип сети передачи данных: WiFin"];
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
- Полученная информация довольна поверхностна, нельзя например получить другие статистики сотовой сети
Если интересно, как это работает, откуда берутся константы и описания структур данных, пишите в комментариях, дополню статью.
Автор: Trahman