// Copyright 2020 The Crashpad Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "util/ios/ios_system_data_collector.h" #include #include #import #include #import #include "base/apple/mach_logging.h" #include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" #include "build/build_config.h" #include "util/mac/sysctl.h" #include "util/misc/clock.h" namespace { template void AddObserver(CFStringRef notification_name, T* observer) { CFNotificationCenterAddObserver( CFNotificationCenterGetLocalCenter(), observer, [](CFNotificationCenterRef center, void* observer_vp, CFNotificationName name, const void* object, CFDictionaryRef userInfo) { T* observer = reinterpret_cast(observer_vp); (observer->*M)(); }, notification_name, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately); } } // namespace namespace crashpad { namespace internal { IOSSystemDataCollector::IOSSystemDataCollector() : major_version_(0), minor_version_(0), patch_version_(0), build_(), machine_description_(), orientation_(0), processor_count_(0), cpu_vendor_(), has_next_daylight_saving_time_(false), is_daylight_saving_time_(false), standard_offset_seconds_(0), daylight_offset_seconds_(0), standard_name_(), daylight_name_(), initialization_time_ns_(ClockMonotonicNanoseconds()) { NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion]; major_version_ = base::saturated_cast(version.majorVersion); minor_version_ = base::saturated_cast(version.minorVersion); patch_version_ = base::saturated_cast(version.patchVersion); processor_count_ = base::saturated_cast([[NSProcessInfo processInfo] processorCount]); build_ = ReadStringSysctlByName("kern.osversion", false); bundle_identifier_ = base::SysNSStringToUTF8([[NSBundle mainBundle] bundleIdentifier]); // If CRASHPAD_IS_IOS_APP_EXTENSION is defined, then the code is compiled with // -fapplication-extension and can only be used in an app extension. Otherwise // check at runtime whether the code is executing in an app extension or not. #if defined(CRASHPAD_IS_IOS_APP_EXTENSION) is_extension_ = true; #else is_extension_ = [[NSBundle mainBundle].bundlePath hasSuffix:@"appex"]; #endif #if defined(ARCH_CPU_X86_64) cpu_vendor_ = ReadStringSysctlByName("machdep.cpu.vendor", false); #endif uint32_t addressable_bits = 0; size_t len = sizeof(uint32_t); // `machdep.virtual_address_size` is the number of addressable bits in // userspace virtual addresses if (sysctlbyname( "machdep.virtual_address_size", &addressable_bits, &len, NULL, 0) != 0) { addressable_bits = 0; } address_mask_ = ~((1UL << addressable_bits) - 1); #if TARGET_OS_SIMULATOR // TODO(justincohen): Consider adding board and model information to // |machine_description| as well (similar to MacModelAndBoard in // util/mac/mac_util.cc). const char* model = getenv("SIMULATOR_MODEL_IDENTIFIER"); if (model == nullptr) { switch ([[UIDevice currentDevice] userInterfaceIdiom]) { case UIUserInterfaceIdiomPhone: model = "iPhone"; break; case UIUserInterfaceIdiomPad: model = "iPad"; break; default: model = "Unknown"; break; } } machine_description_ = base::StringPrintf("iOS Simulator (%s)", model); #elif TARGET_OS_IPHONE utsname uts; if (uname(&uts) == 0) { machine_description_ = uts.machine; } #else #error "Unexpected target type OS." #endif InstallHandlers(); } IOSSystemDataCollector::~IOSSystemDataCollector() { CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetLocalCenter(), this); } void IOSSystemDataCollector::OSVersion(int* major, int* minor, int* bugfix) const { *major = major_version_; *minor = minor_version_; *bugfix = patch_version_; } void IOSSystemDataCollector::InstallHandlers() { // Timezone. AddObserver( (__bridge CFStringRef)NSSystemTimeZoneDidChangeNotification, this); SystemTimeZoneDidChangeNotification(); // Orientation. AddObserver( (__bridge CFStringRef)UIDeviceOrientationDidChangeNotification, this); OrientationDidChangeNotification(); #if !defined(CRASHPAD_IS_IOS_APP_EXTENSION) // Foreground/Background. Extensions shouldn't use UIApplication*. if (!is_extension_) { AddObserver< IOSSystemDataCollector, &IOSSystemDataCollector::ApplicationDidChangeActiveNotification>( (__bridge CFStringRef)UIApplicationDidBecomeActiveNotification, this); AddObserver< IOSSystemDataCollector, &IOSSystemDataCollector::ApplicationDidChangeActiveNotification>( (__bridge CFStringRef)UIApplicationDidEnterBackgroundNotification, this); ApplicationDidChangeActiveNotification(); } #endif } void IOSSystemDataCollector::SystemTimeZoneDidChangeNotification() { NSTimeZone* time_zone = NSTimeZone.localTimeZone; NSDate* transition = [time_zone nextDaylightSavingTimeTransitionAfterDate:[NSDate date]]; if (transition == nil) { has_next_daylight_saving_time_ = false; is_daylight_saving_time_ = false; standard_offset_seconds_ = base::saturated_cast([time_zone secondsFromGMTForDate:transition]); standard_name_ = base::SysNSStringToUTF8([time_zone abbreviation]); daylight_offset_seconds_ = standard_offset_seconds_; daylight_name_ = standard_name_; } else { has_next_daylight_saving_time_ = true; is_daylight_saving_time_ = time_zone.isDaylightSavingTime; if (time_zone.isDaylightSavingTime) { standard_offset_seconds_ = base::saturated_cast( [time_zone secondsFromGMTForDate:transition]); standard_name_ = base::SysNSStringToUTF8([time_zone abbreviationForDate:transition]); daylight_offset_seconds_ = base::saturated_cast([time_zone secondsFromGMT]); daylight_name_ = base::SysNSStringToUTF8([time_zone abbreviation]); } else { standard_offset_seconds_ = base::saturated_cast([time_zone secondsFromGMT]); standard_name_ = base::SysNSStringToUTF8([time_zone abbreviation]); daylight_offset_seconds_ = base::saturated_cast( [time_zone secondsFromGMTForDate:transition]); daylight_name_ = base::SysNSStringToUTF8([time_zone abbreviationForDate:transition]); } } } void IOSSystemDataCollector::OrientationDidChangeNotification() { orientation_ = base::saturated_cast([[UIDevice currentDevice] orientation]); } void IOSSystemDataCollector::ApplicationDidChangeActiveNotification() { #if defined(CRASHPAD_IS_IOS_APP_EXTENSION) NOTREACHED(); #else dispatch_assert_queue_debug(dispatch_get_main_queue()); bool old_active = active_; active_ = [UIApplication sharedApplication].applicationState == UIApplicationStateActive; if (active_ != old_active && active_application_callback_) { active_application_callback_(active_); } #endif } } // namespace internal } // namespace crashpad