393 lines
13 KiB
Objective-C
393 lines
13 KiB
Objective-C
//
|
|
// XSPhoneInfo.m
|
|
// nochange
|
|
//
|
|
// Created by mac on 2024/10/15.
|
|
//
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <IOKit/IOKitLib.h>
|
|
#import <IOKit/ps/IOPowerSources.h>
|
|
#import <IOKit/ps/IOPSKeys.h>
|
|
#import <UIKit/UIKit.h>
|
|
#import <mach/mach.h>
|
|
#import <mach/mach_host.h>
|
|
#import <sys/sysctl.h>
|
|
#import <malloc/malloc.h>
|
|
#import "XSPhoneInfo.h"
|
|
|
|
|
|
@interface XSPhoneInfo()
|
|
{
|
|
|
|
}
|
|
@property (nonatomic, strong) NSTimer *monitorTimer;
|
|
@property (nonatomic, assign) BOOL isMonitoring;
|
|
|
|
|
|
@property (nonatomic, copy) void (^memoryWarningHandler)(double availableMemory);
|
|
@end
|
|
|
|
|
|
@implementation XSPhoneInfo {
|
|
dispatch_source_t _memoryPressureSource;
|
|
natural_t mem_free;
|
|
natural_t mem_total;
|
|
}
|
|
|
|
+(instancetype)sharedInstance
|
|
{
|
|
static XSPhoneInfo* _sharedInstance = nil;
|
|
|
|
static dispatch_once_t oncePredicate;
|
|
dispatch_once (&oncePredicate, ^{
|
|
_sharedInstance = [[XSPhoneInfo alloc] init];
|
|
});
|
|
return _sharedInstance;
|
|
}
|
|
-(instancetype)init {
|
|
if (self = [super init]) {
|
|
[self setupBatteryMonitoring];
|
|
_pageSize = vm_page_size;
|
|
[self startMemoryMonitoring];
|
|
[self updateMemoryInfo];
|
|
self.remoteIp = @"";
|
|
return self;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (void)setupBatteryMonitoring {
|
|
// 允许电池监控
|
|
[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
|
|
|
|
// 注册电池通知
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(batteryLevelDidChange:)
|
|
name:UIDeviceBatteryLevelDidChangeNotification
|
|
object:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(batteryStateDidChange:)
|
|
name:UIDeviceBatteryStateDidChangeNotification
|
|
object:nil];
|
|
}
|
|
- (void)startBatteryMonitoring {
|
|
if (!self.isMonitoring) {
|
|
self.isMonitoring = YES;
|
|
|
|
// 创建定时器定期更新电池信息
|
|
self.monitorTimer = [NSTimer scheduledTimerWithTimeInterval:60.0
|
|
target:self
|
|
selector:@selector(updateBatteryInfo)
|
|
userInfo:nil
|
|
repeats:YES];
|
|
[self updateBatteryInfo];
|
|
}
|
|
}
|
|
- (void)updateBatteryInfo {
|
|
// 更新基本电池信息
|
|
_batteryLevel = [[UIDevice currentDevice] batteryLevel];
|
|
_isCharging = [[UIDevice currentDevice] batteryState] == UIDeviceBatteryStateCharging;
|
|
_isFullyCharged = [[UIDevice currentDevice] batteryState] == UIDeviceBatteryStateFull;
|
|
|
|
// 获取详细电池信息
|
|
[self updateDetailedBatteryInfo];
|
|
//[self updateMemoryInfo];
|
|
}
|
|
|
|
- (void)updateDetailedBatteryInfo {
|
|
@try {
|
|
// 使用IOKit获取电池详细信息
|
|
CFMutableDictionaryRef matching = IOServiceMatching("IOPMPowerSource");
|
|
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
|
|
|
|
if (service) {
|
|
CFMutableDictionaryRef properties = NULL;
|
|
kern_return_t kr = IORegistryEntryCreateCFProperties(service, &properties, kCFAllocatorDefault, 0);
|
|
|
|
if (kr == KERN_SUCCESS) {
|
|
NSDictionary *batteryProps = (__bridge_transfer NSDictionary *)properties;
|
|
|
|
// 更新基本信息
|
|
_batteryLevel = [[batteryProps objectForKey:@"CurrentCapacity"] floatValue] / 100.0;
|
|
_isCharging = [[batteryProps objectForKey:@"IsCharging"] boolValue];
|
|
_isFullyCharged = [[batteryProps objectForKey:@"FullyCharged"] boolValue];
|
|
|
|
// 获取电池容量信息
|
|
_designCapacity = [[batteryProps objectForKey:@"DesignCapacity"] floatValue];
|
|
_maxCapacity = [[batteryProps objectForKey:@"AppleRawMaxCapacity"] floatValue];
|
|
_currentCapacity = [[batteryProps objectForKey:@"CurrentCapacity"] floatValue];
|
|
|
|
// 获取电压信息
|
|
_batteryVoltage = [[batteryProps objectForKey:@"Voltage"] floatValue];
|
|
|
|
// 获取循环次数
|
|
_cycleCount = [[batteryProps objectForKey:@"CycleCount"] integerValue];
|
|
|
|
// 计算健康度
|
|
if (_designCapacity > 0 && _maxCapacity > 0) {
|
|
_batteryHealth = (_maxCapacity / _designCapacity) * 100;
|
|
}
|
|
|
|
|
|
// 更新电池状态描述
|
|
[self updateBatteryStatus:batteryProps];
|
|
}
|
|
|
|
IOObjectRelease(service);
|
|
}
|
|
} @catch (NSException *exception) {
|
|
NSLog(@"Battery info update error: %@", exception);
|
|
}
|
|
}
|
|
- (void)updateBatteryStatus:(NSDictionary *)batteryProps {
|
|
NSMutableString *status = [NSMutableString string];
|
|
|
|
// 基本状态
|
|
if (_isCharging) {
|
|
[status appendString:@"正在充电"];
|
|
} else {
|
|
[status appendString:@"使用电池中"];
|
|
}
|
|
|
|
// 健康状态
|
|
if (_batteryHealth > 0) {
|
|
//[status appendFormat:@"\n健康度: %ld%%", (long)_batteryHealth];
|
|
/*
|
|
if (_batteryHealth < 80) {
|
|
[status appendString:@" (建议更换电池)"];
|
|
} else if (_batteryHealth < 90) {
|
|
[status appendString:@" (性能可能降低)"];
|
|
}
|
|
*/
|
|
}
|
|
|
|
// 循环次数
|
|
if (_cycleCount > 0) {
|
|
//[status appendFormat:@"\n充电次数: %ld", (long)_cycleCount];
|
|
/*
|
|
if (_cycleCount > 500) {
|
|
[status appendString:@" (使用时间较长)"];
|
|
}
|
|
*/
|
|
}
|
|
self.batteryStatus = [status copy];
|
|
}
|
|
|
|
- (NSDictionary *)getBatteryInfo {
|
|
return @{
|
|
@"level": @(self.batteryLevel),
|
|
@"isCharging": @(self.isCharging),
|
|
@"isFullyCharged": @(self.isFullyCharged),
|
|
@"health": @(self.batteryHealth),
|
|
@"cycleCount": @(self.cycleCount),
|
|
@"temperature": @(self.temperature),
|
|
@"voltage": @(self.voltage),
|
|
@"status": self.batteryStatus ?: @"未知"
|
|
};
|
|
}
|
|
|
|
- (void)stopBatteryMonitoring {
|
|
if (self.isMonitoring) {
|
|
self.isMonitoring = NO;
|
|
[self.monitorTimer invalidate];
|
|
self.monitorTimer = nil;
|
|
}
|
|
}
|
|
- (void)batteryLevelDidChange:(NSNotification *)notification {
|
|
[self updateBatteryInfo];
|
|
}
|
|
|
|
- (void)batteryStateDidChange:(NSNotification *)notification {
|
|
[self updateBatteryInfo];
|
|
}
|
|
|
|
#pragma mark - Memory Info Methods
|
|
|
|
- (void)updateMemoryInfo {
|
|
mach_port_t host_port = mach_host_self();
|
|
mach_msg_type_number_t host_size = HOST_VM_INFO_COUNT;
|
|
vm_statistics_data_t vm_stat;
|
|
|
|
if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS) {
|
|
NSLog(@"Failed to fetch vm statistics");
|
|
return;
|
|
}
|
|
|
|
// 计算内存信息
|
|
natural_t mem_used = (vm_stat.active_count +
|
|
vm_stat.inactive_count +
|
|
vm_stat.wire_count) * _pageSize;
|
|
|
|
self->mem_free = vm_stat.free_count * _pageSize;
|
|
self->mem_total = mem_used + mem_free;
|
|
|
|
// 更新属性
|
|
_totalMemory = self->mem_total / (1024.0 * 1024.0 * 1024.0); // Convert to GB
|
|
_availableMemory = self->mem_free / (1024.0 * 1024.0 * 1024.0); // Convert to GB
|
|
_usedMemory = mem_used / (1024.0 * 1024.0 * 1024.0); // Convert to GB
|
|
_memoryUsage = (mem_used * 100.0) / self->mem_total; // Calculate percentage
|
|
}
|
|
|
|
|
|
- (double)getMemoryUsageForPid:(pid_t)pid {
|
|
task_vm_info_data_t vmInfo;
|
|
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
|
|
kern_return_t kernReturn = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&vmInfo, &count);
|
|
|
|
if (kernReturn == KERN_SUCCESS) {
|
|
return (double)vmInfo.phys_footprint / 1024.0 / 1024.0; // Convert to MB
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#pragma mark - Memory Monitoring
|
|
|
|
|
|
- (void)handleCriticalMemoryPressure {
|
|
|
|
|
|
// 发送通知
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"MemoryCriticalNotification"
|
|
object:nil
|
|
userInfo:@{@"availableMemory": @(self.availableMemory)}];
|
|
}
|
|
|
|
- (void)handleWarningMemoryPressure {
|
|
// 发送警告通知
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"MemoryWarningNotification"
|
|
object:nil
|
|
userInfo:@{@"availableMemory": @(self.availableMemory)}];
|
|
}
|
|
|
|
- (void)startMemoryMonitoring {
|
|
// 创建定时器定期检查内存
|
|
self.monitorTimer = [NSTimer scheduledTimerWithTimeInterval:30.0
|
|
target:self
|
|
selector:@selector(checkMemoryStatus)
|
|
userInfo:nil
|
|
repeats:YES];
|
|
}
|
|
|
|
- (void)stopMemoryMonitoring {
|
|
[self.monitorTimer invalidate];
|
|
self.monitorTimer = nil;
|
|
}
|
|
|
|
- (void)checkMemoryStatus {
|
|
[self updateMemoryInfo];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dealloc {
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
[[UIDevice currentDevice] setBatteryMonitoringEnabled:NO];
|
|
[self stopBatteryMonitoring];
|
|
[self stopMemoryMonitoring];
|
|
if (_memoryPressureSource) {
|
|
dispatch_source_cancel(_memoryPressureSource);
|
|
}
|
|
}
|
|
|
|
- (NSString *)IPAddress {
|
|
|
|
NSDictionary *addresses = [self getIPAddresses];
|
|
__block NSString *address;
|
|
[addresses enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
|
|
address = obj;
|
|
if(address) *stop = YES;
|
|
}];
|
|
return address ? address : @"0.0.0.0";
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *)getIPAddresses {
|
|
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
|
|
|
|
// retrieve the current interfaces - returns 0 on success
|
|
struct ifaddrs *interfaces;
|
|
if(!getifaddrs(&interfaces)) {
|
|
// Loop through linked list of interfaces
|
|
struct ifaddrs *interface;
|
|
for(interface=interfaces; interface; interface=interface->ifa_next) {
|
|
if(!(interface->ifa_flags & IFF_UP) || (interface->ifa_flags & IFF_LOOPBACK)) {
|
|
continue; // deeply nested code harder to read
|
|
}
|
|
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
|
|
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
|
|
if(addr && (addr->sin_family==AF_INET)) {
|
|
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
|
|
NSString *type;
|
|
if(addr->sin_family == AF_INET) {
|
|
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
|
|
type = @"ipv4";
|
|
}
|
|
}
|
|
if(type) {
|
|
NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
|
|
addresses[key] = [NSString stringWithUTF8String:addrBuf];
|
|
}
|
|
}
|
|
}
|
|
// Free memory
|
|
freeifaddrs(interfaces);
|
|
}
|
|
return [addresses count] ? addresses : nil;
|
|
}
|
|
|
|
- (NSString *)CurrentThermalState {
|
|
NSProcessInfoThermalState state = [NSProcessInfo processInfo].thermalState;
|
|
|
|
switch (state) {
|
|
case NSProcessInfoThermalStateNominal:
|
|
return @"Nominal";
|
|
case NSProcessInfoThermalStateFair:
|
|
return @"Fair";
|
|
case NSProcessInfoThermalStateSerious:
|
|
return @"Serious";
|
|
case NSProcessInfoThermalStateCritical:
|
|
return @"Critical";
|
|
default:
|
|
return @"Unknown";
|
|
}
|
|
}
|
|
|
|
- (NSString *)DiskSize {
|
|
@try {
|
|
// 磁盘
|
|
NSDictionary<NSFileAttributeKey, id> *info = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil];
|
|
NSLog(@"diskinfo: %@", info);
|
|
int64_t size = [info[NSFileSystemSize] longLongValue]; // 总大小
|
|
int64_t space = [[info objectForKey:NSFileSystemFreeSize] longLongValue];
|
|
|
|
NSString *gb = [NSByteCountFormatter stringFromByteCount:size countStyle:NSByteCountFormatterCountStyleDecimal];
|
|
NSString *space_gb = [NSByteCountFormatter stringFromByteCount:space countStyle:NSByteCountFormatterCountStyleDecimal];
|
|
return [NSString stringWithFormat:@"%@/%@", space_gb,gb];
|
|
} @catch (NSException *exception) {
|
|
return @"";
|
|
}
|
|
}
|
|
|
|
- (NSString *)IPhoneStatus {
|
|
[self updateBatteryInfo];
|
|
[self updateMemoryInfo];
|
|
NSString *gb = [NSByteCountFormatter stringFromByteCount: self->mem_total countStyle:NSByteCountFormatterCountStyleDecimal];
|
|
NSString *space_gb = [NSByteCountFormatter stringFromByteCount:self->mem_free countStyle:NSByteCountFormatterCountStyleDecimal];
|
|
NSString *mem = [NSString stringWithFormat:@"%@/%@", space_gb,gb];
|
|
|
|
NSString *res = [NSString stringWithFormat:@"电池%ld%%,%@;内存%@;磁盘%@",(long)(self.batteryLevel*100), self.batteryStatus,
|
|
mem,self.DiskSize
|
|
];
|
|
return res;
|
|
}
|
|
|
|
|
|
@end
|
|
|