//
|
// UIDevice+YYAdd.m
|
// YYCategories <https://github.com/ibireme/YYCategories>
|
//
|
// Created by ibireme on 13/4/3.
|
// Copyright (c) 2015 ibireme.
|
//
|
// This source code is licensed under the MIT-style license found in the
|
// LICENSE file in the root directory of this source tree.
|
//
|
|
#import "UIDevice+YYAdd.h"
|
#include <sys/socket.h>
|
#include <sys/sysctl.h>
|
#include <net/if.h>
|
#include <net/if_dl.h>
|
#include <mach/mach.h>
|
#include <arpa/inet.h>
|
#include <ifaddrs.h>
|
#import "YYCategoriesMacro.h"
|
#import "NSString+YYAdd.h"
|
|
YYSYNTH_DUMMY_CLASS(UIDevice_YYAdd)
|
|
|
@implementation UIDevice (YYAdd)
|
|
+ (double)systemVersion {
|
static double version;
|
static dispatch_once_t onceToken;
|
dispatch_once(&onceToken, ^{
|
version = [UIDevice currentDevice].systemVersion.doubleValue;
|
});
|
return version;
|
}
|
|
- (BOOL)isPad {
|
static dispatch_once_t one;
|
static BOOL pad;
|
dispatch_once(&one, ^{
|
pad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
|
});
|
return pad;
|
}
|
|
- (BOOL)isSimulator {
|
static dispatch_once_t one;
|
static BOOL simu;
|
dispatch_once(&one, ^{
|
simu = NSNotFound != [[self model] rangeOfString:@"Simulator"].location;
|
});
|
return simu;
|
}
|
|
- (BOOL)isJailbroken {
|
if ([self isSimulator]) return NO; // Dont't check simulator
|
|
// iOS9 URL Scheme query changed ...
|
// NSURL *cydiaURL = [NSURL URLWithString:@"cydia://package"];
|
// if ([[UIApplication sharedApplication] canOpenURL:cydiaURL]) return YES;
|
|
NSArray *paths = @[@"/Applications/Cydia.app",
|
@"/private/var/lib/apt/",
|
@"/private/var/lib/cydia",
|
@"/private/var/stash"];
|
for (NSString *path in paths) {
|
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) return YES;
|
}
|
|
FILE *bash = fopen("/bin/bash", "r");
|
if (bash != NULL) {
|
fclose(bash);
|
return YES;
|
}
|
|
NSString *path = [NSString stringWithFormat:@"/private/%@", [NSString stringWithUUID]];
|
if ([@"test" writeToFile : path atomically : YES encoding : NSUTF8StringEncoding error : NULL]) {
|
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
|
return YES;
|
}
|
|
return NO;
|
}
|
|
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
|
- (BOOL)canMakePhoneCalls {
|
__block BOOL can;
|
static dispatch_once_t onceToken;
|
dispatch_once(&onceToken, ^{
|
can = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel://"]];
|
});
|
return can;
|
}
|
#endif
|
|
- (NSString *)ipAddressWithIfaName:(NSString *)name {
|
if (name.length == 0) return nil;
|
NSString *address = nil;
|
struct ifaddrs *addrs = NULL;
|
if (getifaddrs(&addrs) == 0) {
|
struct ifaddrs *addr = addrs;
|
while (addr) {
|
if ([[NSString stringWithUTF8String:addr->ifa_name] isEqualToString:name]) {
|
sa_family_t family = addr->ifa_addr->sa_family;
|
switch (family) {
|
case AF_INET: { // IPv4
|
char str[INET_ADDRSTRLEN] = {0};
|
inet_ntop(family, &(((struct sockaddr_in *)addr->ifa_addr)->sin_addr), str, sizeof(str));
|
if (strlen(str) > 0) {
|
address = [NSString stringWithUTF8String:str];
|
}
|
} break;
|
|
case AF_INET6: { // IPv6
|
char str[INET6_ADDRSTRLEN] = {0};
|
inet_ntop(family, &(((struct sockaddr_in6 *)addr->ifa_addr)->sin6_addr), str, sizeof(str));
|
if (strlen(str) > 0) {
|
address = [NSString stringWithUTF8String:str];
|
}
|
}
|
|
default: break;
|
}
|
if (address) break;
|
}
|
addr = addr->ifa_next;
|
}
|
}
|
freeifaddrs(addrs);
|
return address;
|
}
|
|
- (NSString *)ipAddressWIFI {
|
return [self ipAddressWithIfaName:@"en0"];
|
}
|
|
- (NSString *)ipAddressCell {
|
return [self ipAddressWithIfaName:@"pdp_ip0"];
|
}
|
|
|
typedef struct {
|
uint64_t en_in;
|
uint64_t en_out;
|
uint64_t pdp_ip_in;
|
uint64_t pdp_ip_out;
|
uint64_t awdl_in;
|
uint64_t awdl_out;
|
} yy_net_interface_counter;
|
|
|
static uint64_t yy_net_counter_add(uint64_t counter, uint64_t bytes) {
|
if (bytes < (counter % 0xFFFFFFFF)) {
|
counter += 0xFFFFFFFF - (counter % 0xFFFFFFFF);
|
counter += bytes;
|
} else {
|
counter = bytes;
|
}
|
return counter;
|
}
|
|
static uint64_t yy_net_counter_get_by_type(yy_net_interface_counter *counter, YYNetworkTrafficType type) {
|
uint64_t bytes = 0;
|
if (type & YYNetworkTrafficTypeWWANSent) bytes += counter->pdp_ip_out;
|
if (type & YYNetworkTrafficTypeWWANReceived) bytes += counter->pdp_ip_in;
|
if (type & YYNetworkTrafficTypeWIFISent) bytes += counter->en_out;
|
if (type & YYNetworkTrafficTypeWIFIReceived) bytes += counter->en_in;
|
if (type & YYNetworkTrafficTypeAWDLSent) bytes += counter->awdl_out;
|
if (type & YYNetworkTrafficTypeAWDLReceived) bytes += counter->awdl_in;
|
return bytes;
|
}
|
|
static yy_net_interface_counter yy_get_net_interface_counter() {
|
static dispatch_semaphore_t lock;
|
static NSMutableDictionary *sharedInCounters;
|
static NSMutableDictionary *sharedOutCounters;
|
static dispatch_once_t onceToken;
|
dispatch_once(&onceToken, ^{
|
sharedInCounters = [NSMutableDictionary new];
|
sharedOutCounters = [NSMutableDictionary new];
|
lock = dispatch_semaphore_create(1);
|
});
|
|
yy_net_interface_counter counter = {0};
|
struct ifaddrs *addrs;
|
const struct ifaddrs *cursor;
|
if (getifaddrs(&addrs) == 0) {
|
cursor = addrs;
|
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
|
while (cursor) {
|
if (cursor->ifa_addr->sa_family == AF_LINK) {
|
const struct if_data *data = cursor->ifa_data;
|
NSString *name = cursor->ifa_name ? [NSString stringWithUTF8String:cursor->ifa_name] : nil;
|
if (name) {
|
uint64_t counter_in = ((NSNumber *)sharedInCounters[name]).unsignedLongLongValue;
|
counter_in = yy_net_counter_add(counter_in, data->ifi_ibytes);
|
sharedInCounters[name] = @(counter_in);
|
|
uint64_t counter_out = ((NSNumber *)sharedOutCounters[name]).unsignedLongLongValue;
|
counter_out = yy_net_counter_add(counter_out, data->ifi_obytes);
|
sharedOutCounters[name] = @(counter_out);
|
|
if ([name hasPrefix:@"en"]) {
|
counter.en_in += counter_in;
|
counter.en_out += counter_out;
|
} else if ([name hasPrefix:@"awdl"]) {
|
counter.awdl_in += counter_in;
|
counter.awdl_out += counter_out;
|
} else if ([name hasPrefix:@"pdp_ip"]) {
|
counter.pdp_ip_in += counter_in;
|
counter.pdp_ip_out += counter_out;
|
}
|
}
|
}
|
cursor = cursor->ifa_next;
|
}
|
dispatch_semaphore_signal(lock);
|
freeifaddrs(addrs);
|
}
|
|
return counter;
|
}
|
|
- (uint64_t)getNetworkTrafficBytes:(YYNetworkTrafficType)types {
|
yy_net_interface_counter counter = yy_get_net_interface_counter();
|
return yy_net_counter_get_by_type(&counter, types);
|
}
|
|
- (NSString *)machineModel {
|
static dispatch_once_t one;
|
static NSString *model;
|
dispatch_once(&one, ^{
|
size_t size;
|
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
|
char *machine = malloc(size);
|
sysctlbyname("hw.machine", machine, &size, NULL, 0);
|
model = [NSString stringWithUTF8String:machine];
|
free(machine);
|
});
|
return model;
|
}
|
|
- (NSString *)machineModelName {
|
static dispatch_once_t one;
|
static NSString *name;
|
dispatch_once(&one, ^{
|
NSString *model = [self machineModel];
|
if (!model) return;
|
NSDictionary *dic = @{
|
@"Watch1,1" : @"Apple Watch 38mm",
|
@"Watch1,2" : @"Apple Watch 42mm",
|
@"Watch2,3" : @"Apple Watch Series 2 38mm",
|
@"Watch2,4" : @"Apple Watch Series 2 42mm",
|
@"Watch2,6" : @"Apple Watch Series 1 38mm",
|
@"Watch1,7" : @"Apple Watch Series 1 42mm",
|
|
@"iPod1,1" : @"iPod touch 1",
|
@"iPod2,1" : @"iPod touch 2",
|
@"iPod3,1" : @"iPod touch 3",
|
@"iPod4,1" : @"iPod touch 4",
|
@"iPod5,1" : @"iPod touch 5",
|
@"iPod7,1" : @"iPod touch 6",
|
|
@"iPhone1,1" : @"iPhone 1G",
|
@"iPhone1,2" : @"iPhone 3G",
|
@"iPhone2,1" : @"iPhone 3GS",
|
@"iPhone3,1" : @"iPhone 4 (GSM)",
|
@"iPhone3,2" : @"iPhone 4",
|
@"iPhone3,3" : @"iPhone 4 (CDMA)",
|
@"iPhone4,1" : @"iPhone 4S",
|
@"iPhone5,1" : @"iPhone 5",
|
@"iPhone5,2" : @"iPhone 5",
|
@"iPhone5,3" : @"iPhone 5c",
|
@"iPhone5,4" : @"iPhone 5c",
|
@"iPhone6,1" : @"iPhone 5s",
|
@"iPhone6,2" : @"iPhone 5s",
|
@"iPhone7,1" : @"iPhone 6 Plus",
|
@"iPhone7,2" : @"iPhone 6",
|
@"iPhone8,1" : @"iPhone 6s",
|
@"iPhone8,2" : @"iPhone 6s Plus",
|
@"iPhone8,4" : @"iPhone SE",
|
@"iPhone9,1" : @"iPhone 7",
|
@"iPhone9,2" : @"iPhone 7 Plus",
|
@"iPhone9,3" : @"iPhone 7",
|
@"iPhone9,4" : @"iPhone 7 Plus",
|
|
@"iPad1,1" : @"iPad 1",
|
@"iPad2,1" : @"iPad 2 (WiFi)",
|
@"iPad2,2" : @"iPad 2 (GSM)",
|
@"iPad2,3" : @"iPad 2 (CDMA)",
|
@"iPad2,4" : @"iPad 2",
|
@"iPad2,5" : @"iPad mini 1",
|
@"iPad2,6" : @"iPad mini 1",
|
@"iPad2,7" : @"iPad mini 1",
|
@"iPad3,1" : @"iPad 3 (WiFi)",
|
@"iPad3,2" : @"iPad 3 (4G)",
|
@"iPad3,3" : @"iPad 3 (4G)",
|
@"iPad3,4" : @"iPad 4",
|
@"iPad3,5" : @"iPad 4",
|
@"iPad3,6" : @"iPad 4",
|
@"iPad4,1" : @"iPad Air",
|
@"iPad4,2" : @"iPad Air",
|
@"iPad4,3" : @"iPad Air",
|
@"iPad4,4" : @"iPad mini 2",
|
@"iPad4,5" : @"iPad mini 2",
|
@"iPad4,6" : @"iPad mini 2",
|
@"iPad4,7" : @"iPad mini 3",
|
@"iPad4,8" : @"iPad mini 3",
|
@"iPad4,9" : @"iPad mini 3",
|
@"iPad5,1" : @"iPad mini 4",
|
@"iPad5,2" : @"iPad mini 4",
|
@"iPad5,3" : @"iPad Air 2",
|
@"iPad5,4" : @"iPad Air 2",
|
@"iPad6,3" : @"iPad Pro (9.7 inch)",
|
@"iPad6,4" : @"iPad Pro (9.7 inch)",
|
@"iPad6,7" : @"iPad Pro (12.9 inch)",
|
@"iPad6,8" : @"iPad Pro (12.9 inch)",
|
|
@"AppleTV2,1" : @"Apple TV 2",
|
@"AppleTV3,1" : @"Apple TV 3",
|
@"AppleTV3,2" : @"Apple TV 3",
|
@"AppleTV5,3" : @"Apple TV 4",
|
|
@"i386" : @"Simulator x86",
|
@"x86_64" : @"Simulator x64",
|
};
|
name = dic[model];
|
if (!name) name = model;
|
});
|
return name;
|
}
|
|
- (NSDate *)systemUptime {
|
NSTimeInterval time = [[NSProcessInfo processInfo] systemUptime];
|
return [[NSDate alloc] initWithTimeIntervalSinceNow:(0 - time)];
|
}
|
|
- (int64_t)diskSpace {
|
NSError *error = nil;
|
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
|
if (error) return -1;
|
int64_t space = [[attrs objectForKey:NSFileSystemSize] longLongValue];
|
if (space < 0) space = -1;
|
return space;
|
}
|
|
- (int64_t)diskSpaceFree {
|
NSError *error = nil;
|
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
|
if (error) return -1;
|
int64_t space = [[attrs objectForKey:NSFileSystemFreeSize] longLongValue];
|
if (space < 0) space = -1;
|
return space;
|
}
|
|
- (int64_t)diskSpaceUsed {
|
int64_t total = self.diskSpace;
|
int64_t free = self.diskSpaceFree;
|
if (total < 0 || free < 0) return -1;
|
int64_t used = total - free;
|
if (used < 0) used = -1;
|
return used;
|
}
|
|
- (int64_t)memoryTotal {
|
int64_t mem = [[NSProcessInfo processInfo] physicalMemory];
|
if (mem < -1) mem = -1;
|
return mem;
|
}
|
|
- (int64_t)memoryUsed {
|
mach_port_t host_port = mach_host_self();
|
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
|
vm_size_t page_size;
|
vm_statistics_data_t vm_stat;
|
kern_return_t kern;
|
|
kern = host_page_size(host_port, &page_size);
|
if (kern != KERN_SUCCESS) return -1;
|
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
|
if (kern != KERN_SUCCESS) return -1;
|
return page_size * (vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count);
|
}
|
|
- (int64_t)memoryFree {
|
mach_port_t host_port = mach_host_self();
|
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
|
vm_size_t page_size;
|
vm_statistics_data_t vm_stat;
|
kern_return_t kern;
|
|
kern = host_page_size(host_port, &page_size);
|
if (kern != KERN_SUCCESS) return -1;
|
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
|
if (kern != KERN_SUCCESS) return -1;
|
return vm_stat.free_count * page_size;
|
}
|
|
- (int64_t)memoryActive {
|
mach_port_t host_port = mach_host_self();
|
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
|
vm_size_t page_size;
|
vm_statistics_data_t vm_stat;
|
kern_return_t kern;
|
|
kern = host_page_size(host_port, &page_size);
|
if (kern != KERN_SUCCESS) return -1;
|
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
|
if (kern != KERN_SUCCESS) return -1;
|
return vm_stat.active_count * page_size;
|
}
|
|
- (int64_t)memoryInactive {
|
mach_port_t host_port = mach_host_self();
|
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
|
vm_size_t page_size;
|
vm_statistics_data_t vm_stat;
|
kern_return_t kern;
|
|
kern = host_page_size(host_port, &page_size);
|
if (kern != KERN_SUCCESS) return -1;
|
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
|
if (kern != KERN_SUCCESS) return -1;
|
return vm_stat.inactive_count * page_size;
|
}
|
|
- (int64_t)memoryWired {
|
mach_port_t host_port = mach_host_self();
|
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
|
vm_size_t page_size;
|
vm_statistics_data_t vm_stat;
|
kern_return_t kern;
|
|
kern = host_page_size(host_port, &page_size);
|
if (kern != KERN_SUCCESS) return -1;
|
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
|
if (kern != KERN_SUCCESS) return -1;
|
return vm_stat.wire_count * page_size;
|
}
|
|
- (int64_t)memoryPurgable {
|
mach_port_t host_port = mach_host_self();
|
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
|
vm_size_t page_size;
|
vm_statistics_data_t vm_stat;
|
kern_return_t kern;
|
|
kern = host_page_size(host_port, &page_size);
|
if (kern != KERN_SUCCESS) return -1;
|
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
|
if (kern != KERN_SUCCESS) return -1;
|
return vm_stat.purgeable_count * page_size;
|
}
|
|
- (NSUInteger)cpuCount {
|
return [NSProcessInfo processInfo].activeProcessorCount;
|
}
|
|
- (float)cpuUsage {
|
float cpu = 0;
|
NSArray *cpus = [self cpuUsagePerProcessor];
|
if (cpus.count == 0) return -1;
|
for (NSNumber *n in cpus) {
|
cpu += n.floatValue;
|
}
|
return cpu;
|
}
|
|
- (NSArray *)cpuUsagePerProcessor {
|
processor_info_array_t _cpuInfo, _prevCPUInfo = nil;
|
mach_msg_type_number_t _numCPUInfo, _numPrevCPUInfo = 0;
|
unsigned _numCPUs;
|
NSLock *_cpuUsageLock;
|
|
int _mib[2U] = { CTL_HW, HW_NCPU };
|
size_t _sizeOfNumCPUs = sizeof(_numCPUs);
|
int _status = sysctl(_mib, 2U, &_numCPUs, &_sizeOfNumCPUs, NULL, 0U);
|
if (_status)
|
_numCPUs = 1;
|
|
_cpuUsageLock = [[NSLock alloc] init];
|
|
natural_t _numCPUsU = 0U;
|
kern_return_t err = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &_numCPUsU, &_cpuInfo, &_numCPUInfo);
|
if (err == KERN_SUCCESS) {
|
[_cpuUsageLock lock];
|
|
NSMutableArray *cpus = [NSMutableArray new];
|
for (unsigned i = 0U; i < _numCPUs; ++i) {
|
Float32 _inUse, _total;
|
if (_prevCPUInfo) {
|
_inUse = (
|
(_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER])
|
+ (_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM])
|
+ (_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE] - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE])
|
);
|
_total = _inUse + (_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE] - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE]);
|
} else {
|
_inUse = _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] + _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] + _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE];
|
_total = _inUse + _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE];
|
}
|
[cpus addObject:@(_inUse / _total)];
|
}
|
|
[_cpuUsageLock unlock];
|
if (_prevCPUInfo) {
|
size_t prevCpuInfoSize = sizeof(integer_t) * _numPrevCPUInfo;
|
vm_deallocate(mach_task_self(), (vm_address_t)_prevCPUInfo, prevCpuInfoSize);
|
}
|
return cpus;
|
} else {
|
return nil;
|
}
|
}
|
|
@end
|