1// Copyright 2020 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#include "aemu/base/Optional.h" 16#include "aemu/base/system/Memory.h" 17#include "aemu/base/system/System.h" 18 19#include <CoreFoundation/CoreFoundation.h> 20#include <IOKit/IOBSD.h> 21#include <IOKit/IOKitLib.h> 22#include <AppKit/AppKit.h> 23 24#include <IOKit/kext/KextManager.h> 25#include <IOKit/storage/IOBlockStorageDevice.h> 26 27#import <Foundation/Foundation.h> 28#import <Foundation/NSProcessInfo.h> 29 30#include <mach/mach_init.h> 31#include <mach/thread_act.h> 32#include <mach/mach_port.h> 33 34#include <stdlib.h> 35#include <sys/stat.h> 36 37namespace android { 38namespace base { 39 40// From: 41// https://stackoverflow.com/questions/49677034/thread-sleeps-too-long-on-os-x-when-in-background 42void disableAppNap_macImpl(void) { 43 if ([[NSProcessInfo processInfo] 44 respondsToSelector:@selector(beginActivityWithOptions:reason:)]) { 45 [[NSProcessInfo processInfo] 46 beginActivityWithOptions:0x00FFFFFF 47 reason:@"Not sleepy and don't want to nap"]; 48 } 49} 50 51// based on https://stackoverflow.com/questions/13893134/get-current-pthread-cpu-usage-mac-os-x 52void cpuUsageCurrentThread_macImpl( 53 uint64_t* user, 54 uint64_t* sys) { 55 56 kern_return_t kr; 57 mach_msg_type_number_t count; 58 mach_port_t thread; 59 thread_basic_info_data_t info; 60 61 thread = mach_thread_self(); 62 63 count = THREAD_BASIC_INFO_COUNT; 64 kr = thread_info( 65 thread, 66 THREAD_BASIC_INFO, 67 (thread_info_t)&info, 68 &count); 69 70 if (kr == KERN_SUCCESS) { 71 *user = 72 info.user_time.seconds * 1000000ULL + 73 info.user_time.microseconds; 74 *sys = 75 info.system_time.seconds * 1000000ULL + 76 info.system_time.microseconds; 77 } else { 78 fprintf(stderr, "%s: WARNING: could not get thread info for CPU usage\n", __func__); 79 } 80 81 mach_port_deallocate(mach_task_self(), thread); 82} 83 84Optional<DiskKind> nativeDiskKind(int st_dev) { 85 const char* devName = devname(st_dev, S_IFBLK); 86 if (!devName) { 87 return {}; 88 } 89 CFMutableDictionaryRef classesToMatch = 90 IOBSDNameMatching(kIOMasterPortDefault, 0, devName); 91 if (!classesToMatch) { 92 NSLog(@"Could not find io classes of disk"); 93 return {}; 94 } 95 96 // get iterator of matching services 97 io_iterator_t entryIterator; 98 99 if (KERN_SUCCESS != IOServiceGetMatchingServices(kIOMasterPortDefault, 100 classesToMatch, 101 &entryIterator)) { 102 NSLog(@"Can't iterate services"); 103 return {}; 104 } 105 106 // iterate over all found medias 107 io_object_t serviceEntry, parentMedia; 108 while ((serviceEntry = IOIteratorNext(entryIterator)) != 0) { 109 // We assume there won't be more levels of nesting here. The limit is 110 // arbitrary and can be increased if we hit it. 111 int maxlevels = 8; 112 do { 113 kern_return_t kernResult = IORegistryEntryGetParentEntry( 114 serviceEntry, kIOServicePlane, &parentMedia); 115 IOObjectRelease(serviceEntry); 116 117 if (KERN_SUCCESS != kernResult) { 118 serviceEntry = 0; 119 NSLog(@"Error while getting parent service entry"); 120 break; 121 } 122 123 serviceEntry = parentMedia; 124 if (!parentMedia) { 125 break; // finished iterator 126 } 127 128 CFTypeRef res = IORegistryEntryCreateCFProperty( 129 serviceEntry, CFSTR(kIOPropertyDeviceCharacteristicsKey), 130 kCFAllocatorDefault, 0); 131 if (res) { 132 NSString* type = [(NSDictionary*)res 133 objectForKey:(id)CFSTR(kIOPropertyMediumTypeKey)]; 134 if ([@kIOPropertyMediumTypeSolidStateKey 135 isEqualToString:type]) { 136 CFRelease(res); 137 return DiskKind::Ssd; 138 } else if ([@kIOPropertyMediumTypeRotationalKey 139 isEqualToString:type]) { 140 CFRelease(res); 141 return DiskKind::Hdd; 142 } 143 CFRelease(res); 144 } 145 } while (maxlevels--); 146 147 if (serviceEntry) { 148 IOObjectRelease(serviceEntry); 149 } 150 } 151 IOObjectRelease(entryIterator); 152 153 return {}; 154} 155 156// From: 157// https://stackoverflow.com/questions/6796028/start-a-gui-process-in-mac-os-x-without-dock-icon 158void hideDockIcon_macImpl(void) { 159 if (NSApp == nil) { 160 // Initialize the global variable "NSApp" 161 [NSApplication sharedApplication]; 162 } 163 [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; 164 165} 166 167} // namespace base 168} // namespace android 169