iOS でバッテリ残量を取得するAPIについて調査しました.作業メモを公開します
結論から言うと
- iOS 11までは隠しAPIが存在.残量,充電回数(サイクル数)など詳細が取得できた.
- iOS 12でもAPIはあるが,情報がほぼ何も取れなくなった.
- iOS 12でもエミュレータなら,従来通りの情報が取れる.
と言う感じでした
サンプルコード
Object-c で書きました.
まず隠しAPIのエントリポイントを見つけて,情報を取り出す処理です.
概要としては,内部で dlopen関数 で /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit というライブラリをダイナミックロードして,その中になる関数を呼び出します.
#import <Foundation/Foundation.h> #include <dlfcn.h> NSDictionary *FCPrivateBatteryStatus() { static mach_port_t *s_kIOMasterPortDefault; static kern_return_t (*s_IORegistryEntryCreateCFProperties)(mach_port_t entry, CFMutableDictionaryRef *properties, CFAllocatorRef allocator, UInt32 options); static mach_port_t (*s_IOServiceGetMatchingService)(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT); static CFMutableDictionaryRef (*s_IOServiceMatching)(const char *name); static CFMutableDictionaryRef g_powerSourceService; static mach_port_t g_platformExpertDevice; static BOOL foundSymbols = NO; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ void* handle = dlopen("/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", RTLD_LAZY); s_IORegistryEntryCreateCFProperties = dlsym(handle, "IORegistryEntryCreateCFProperties"); s_kIOMasterPortDefault = dlsym(handle, "kIOMasterPortDefault"); s_IOServiceMatching = dlsym(handle, "IOServiceMatching"); s_IOServiceGetMatchingService = dlsym(handle, "IOServiceGetMatchingService"); if (s_IORegistryEntryCreateCFProperties && s_IOServiceMatching && s_IOServiceGetMatchingService) { g_powerSourceService = s_IOServiceMatching("IOPMPowerSource"); g_platformExpertDevice = s_IOServiceGetMatchingService(*s_kIOMasterPortDefault, g_powerSourceService); foundSymbols = (g_powerSourceService && g_platformExpertDevice); } }); if (! foundSymbols) return nil; CFMutableDictionaryRef prop = NULL; s_IORegistryEntryCreateCFProperties(g_platformExpertDevice, &prop, 0, 0); return prop ? ((NSDictionary *) CFBridgingRelease(prop)) : nil; }
使い方は
NSDictionary *prop = FCPrivateBatteryStatus();
という感じです.情報は NSDictionary に格納されています.
NSDictionaryの中身を見るために,簡単な iOSのアプリを書きました.
と言っても UITextView を使ってNSDictionaryの中身を全部テキストで表示するだけのアプリです.
コードはこんな感じ. viewDidLoad を用意するだけです.
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSDictionary *prop = FCPrivateBatteryStatus(); UITextView *text = [[UITextView alloc] init]; NSMutableString *str = [NSMutableString string]; for (id key in [prop keyEnumerator]) { NSLog(@"Key:%@ Value:%@", key, [prop valueForKey:key]); [str appendString: [NSString stringWithFormat:@"Key:%@ Value:%@ \n", key, [prop valueForKey:key] ]]; } text.text = str; [text sizeToFit]; [self.view addSubview:text]; }
iOS 12以降の実機で実行すると
- 充電中か,否か
- バッテリーを搭載しているか,否か
しか表示されませんでした.
エミュレータで実行すると
.... 省略.... LegacyBatteryInfo { Amperage = 2181; Capacity = 5917; Current = 4240; "Cycle Count" = 384; Flags = 7; Voltage = 8370; } .... 省略....
という感じで情報が取れるんですが...Appleの方針なので諦めるしかないようです.
絶対に挫折しないiPhoneアプリ開発「超」入門 第7版 【Xcode 10 & iOS 12】 完全対応 (Informatics&IDEA)
posted with amazlet at 19.02.06