Skip to content

Commit

Permalink
Update SMCService
Browse files Browse the repository at this point in the history
  • Loading branch information
rurza committed May 10, 2024
1 parent 9e32501 commit 11657e3
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 65 deletions.
18 changes: 9 additions & 9 deletions BatFi.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -508,11 +508,11 @@
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 49;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = YL78V8PY6H;
ENABLE_HARDENED_RUNTIME = YES;
MARKETING_VERSION = "";
MARKETING_VERSION = $APP_VERSION;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
Expand All @@ -525,11 +525,11 @@
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 49;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = YL78V8PY6H;
ENABLE_HARDENED_RUNTIME = YES;
MARKETING_VERSION = "";
MARKETING_VERSION = $APP_VERSION;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
Expand Down Expand Up @@ -664,7 +664,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = YL78V8PY6H;
ENABLE_HARDENED_RUNTIME = YES;
Expand Down Expand Up @@ -698,7 +698,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = YL78V8PY6H;
ENABLE_HARDENED_RUNTIME = YES;
Expand Down Expand Up @@ -788,7 +788,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = YL78V8PY6H;
ENABLE_HARDENED_RUNTIME = YES;
Expand Down Expand Up @@ -819,11 +819,11 @@
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 49;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = YL78V8PY6H;
ENABLE_HARDENED_RUNTIME = YES;
MARKETING_VERSION = "";
MARKETING_VERSION = $APP_VERSION;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
Expand Down
2 changes: 2 additions & 0 deletions BatFiKit/Sources/Clients/SentryClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public struct SentryClient {
public var startSDK: @Sendable () -> Void = { }
public var captureMessage: @Sendable (_ message: String) -> Void = { _ in }
public var captureError: @Sendable (_ error: Error) -> Void = { _ in }
public var addBreadcrumb: @Sendable (_ category: String, _ message: String) -> Void = { _, _ in }
public var closeSDK: @Sendable () -> Void = { }
}

extension SentryClient: TestDependencyKey {
Expand Down
13 changes: 13 additions & 0 deletions BatFiKit/Sources/ClientsLive/SentryClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,29 @@ extension Clients.SentryClient: DependencyKey {
options.debug = true
#endif

let releaseVersionNumber = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
let buildVersionNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String

options.tracesSampleRate = 1.0
options.diagnosticLevel = .warning
options.enableMetricKit = true
options.appHangTimeoutInterval = 3
options.releaseName = "BatFi@\(releaseVersionNumber ?? "Unknown")@\(buildVersionNumber ?? "Unknown")"
}
},
captureMessage: { message in
SentrySDK.capture(message: message)
},
captureError: { error in
SentrySDK.capture(error: error)
},
addBreadcrumb: { category, message in
let breadcrumb = Breadcrumb(level: .info, category: category)
breadcrumb.message = message
SentrySDK.addBreadcrumb(breadcrumb)
},
closeSDK: {
SentrySDK.close()
}
)
}()
Expand Down
4 changes: 3 additions & 1 deletion BatFiKit/Sources/Server/HelperPropertyList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import EmbeddedPropertyList
import Foundation

struct HelperPropertyList: Decodable {
public let build: String
public let version: String
public let authorizedClients: [String]

private enum CodingKeys: String, CodingKey {
case version = "CFBundleVersion"
case version = "CFBundleShortVersionString"
case build = "CFBundleVersion"
case authorizedClients = "SMAuthorizedClients"
}
}
2 changes: 1 addition & 1 deletion BatFiKit/Sources/Server/Listener.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ final class ListenerDelegate: NSObject, NSXPCListenerDelegate {

final class XPCServiceHandler: XPCService {
private lazy var logger = Logger(subsystem: Constant.helperBundleIdentifier, category: "XPCServiceHandler")
private lazy var smcService = SMCService()
private lazy var smcService = SMCService.shared

func setForceDischarge(_ reply: @escaping ((any Error)?) -> Void) {
changeChargingMode(.forceDischarging, reply: reply)
Expand Down
124 changes: 81 additions & 43 deletions BatFiKit/Sources/Server/SMCService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,15 @@ actor SMCService {
private lazy var logger = Logger(subsystem: Constant.helperBundleIdentifier, category: "SMC Service")
private var smcIsOpened = false

init() {
Task {
await openSMCIfNeeded()
}
}
static let shared = SMCService()

private init() { }

func close() {
SMCKit.close()
}

func setChargingMode(_ message: SMCChargingCommand) async throws {
openSMCIfNeeded()
let disableChargingByte: UInt8
let inhibitChargingByte: UInt8
let enableSystemChargeLimitByte: UInt8
Expand Down Expand Up @@ -53,16 +50,18 @@ actor SMCService {
logger.notice("Handling enable system charge limit")
}

logger.notice("Setting SMC charging status")
await openSMCIfNeeded()

do {
try SMCKit.writeData(.disableCharging, uint8: disableChargingByte)
try SMCKit.writeData(.inhibitChargingC, uint8: inhibitChargingByte)
try SMCKit.writeData(.inhibitChargingB, uint8: inhibitChargingByte)
try SMCKit.writeData(.enableSystemChargeLimit, uint8: enableSystemChargeLimitByte)
} catch {
logger.critical("SMC writing error: \(error)")
SentrySDK.capture(error: error)
resetIfPossible()
throw error
self.logger.critical("SMC writing error: \(error)")
self.resetIfPossible()
smcIsOpened = false
}
}

Expand All @@ -73,73 +72,112 @@ actor SMCService {
try SMCKit.writeData(.inhibitChargingB, uint8: 0)
try SMCKit.writeData(.enableSystemChargeLimit, uint8: 0)
} catch {
smcIsOpened = false
logger.critical("Resetting charging state failed. \(error)")
SentrySDK.capture(error: error)
}
}

func smcChargingStatus() async throws -> SMCChargingStatus {
openSMCIfNeeded()
let forceDischarging = try SMCKit.readData(SMCKey.disableCharging)
let inhibitChargingC = try SMCKit.readData(SMCKey.inhibitChargingC)
let inhibitChargingB = try SMCKit.readData(SMCKey.inhibitChargingB)
let lidClosed = try SMCKit.readData(SMCKey.lidClosed)

logger.notice("Checking SMC status")

return SMCChargingStatus(
forceDischarging: forceDischarging.0 == 01,
inhitbitCharging: (inhibitChargingC.0 == 02 && inhibitChargingB.0 == 02)
await openSMCIfNeeded()
do {
let forceDischarging = try SMCKit.readData(SMCKey.disableCharging)
let inhibitChargingC = try SMCKit.readData(SMCKey.inhibitChargingC)
let inhibitChargingB = try SMCKit.readData(SMCKey.inhibitChargingB)
let lidClosed = try SMCKit.readData(SMCKey.lidClosed)

return SMCChargingStatus(
forceDischarging: forceDischarging.0 == 01,
inhitbitCharging: (inhibitChargingC.0 == 02 && inhibitChargingB.0 == 02)
|| (inhibitChargingC.0 == 03 && inhibitChargingB.0 == 03),
lidClosed: lidClosed.0 == 01
)
lidClosed: lidClosed.0 == 01
)
} catch {
smcIsOpened = false
throw error
}
}

func magsafeLEDColor(_ option: MagSafeLEDOption) async throws -> MagSafeLEDOption {
openSMCIfNeeded()
try SMCKit.writeData(SMCKey.magSafeLED, uint8: option.rawValue)
logger.notice("Setting MagSafe LED color")
await openSMCIfNeeded()
do {
try SMCKit.writeData(SMCKey.magSafeLED, uint8: option.rawValue)
let data = try SMCKit.readData(.magSafeLED)
guard let option = MagSafeLEDOption(rawValue: data.0) else {
throw SMCError.canNotCreateMagSafeLEDOption
}
return option
} catch {
SentrySDK.capture(error: error)
smcIsOpened = false
throw error
}

}

func getPowerDistribution() async throws -> PowerDistributionInfo {
openSMCIfNeeded()
let rawBatteryPower = try SMCKit.readData(SMCKey.batteryPower)
let rawExternalPower = try SMCKit.readData(SMCKey.externalPower)
logger.notice("Getting power distribution")
await openSMCIfNeeded()
do {
let rawBatteryPower = try SMCKit.readData(SMCKey.batteryPower)
let rawExternalPower = try SMCKit.readData(SMCKey.externalPower)

var batteryPower = Float(fromBytes: (rawBatteryPower.0, rawBatteryPower.1, rawBatteryPower.2, rawBatteryPower.3))
var externalPower = Float(fromBytes: (rawExternalPower.0, rawExternalPower.1, rawExternalPower.2, rawExternalPower.3))
var batteryPower = Float(fromBytes: (rawBatteryPower.0, rawBatteryPower.1, rawBatteryPower.2, rawBatteryPower.3))
var externalPower = Float(fromBytes: (rawExternalPower.0, rawExternalPower.1, rawExternalPower.2, rawExternalPower.3))

if abs(batteryPower) < 0.01 {
batteryPower = 0
}
if externalPower < 0.01 {
externalPower = 0
if abs(batteryPower) < 0.01 {
batteryPower = 0
}
if externalPower < 0.01 {
externalPower = 0
}

let systemPower = batteryPower + externalPower

return PowerDistributionInfo(batteryPower: batteryPower, externalPower: externalPower, systemPower: systemPower)
} catch {
smcIsOpened = false
throw error
}
}


let systemPower = batteryPower + externalPower
private func openSMCIfNeeded() async {
guard !self.smcIsOpened else {
logger.notice("SMC is already opened.")
return
}

return PowerDistributionInfo(batteryPower: batteryPower, externalPower: externalPower, systemPower: systemPower)
logger.notice("Opening SMC...")
await attemptToOpenSMC(withRetryAttempts: 3)
}

private func openSMCIfNeeded() {
if !smcIsOpened {
private func attemptToOpenSMC(withRetryAttempts attempts: Int) async {
var currentAttempt = 0

while currentAttempt < attempts {
do {
try SMCKit.open()
smcIsOpened = true
try await openSMC()
logger.notice("SMC successfully opened!")
self.smcIsOpened = true
return
} catch {
SentrySDK.capture(error: error)
currentAttempt += 1
if currentAttempt < attempts {
logger.error("Failed to open SMC, retrying... (\(currentAttempt)/\(attempts))")
try? await Task.sleep(for: .seconds(1))
} else {
logger.error("Failed to open SMC after \(attempts) attempts. Giving up...")
logger.critical("SMC opening error: \(error)")
SentrySDK.capture(error: error)
return
}
}
}
}

private func openSMC() async throws {
logger.notice("Attempting to open SMC...")
try SMCKit.open()
}
}
4 changes: 3 additions & 1 deletion BatFiKit/Sources/Server/Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public final class Server {
public func start() throws {
let data = try EmbeddedPropertyListReader.info.readInternal()
let plist = try PropertyListDecoder().decode(HelperPropertyList.self, from: data)
logger.notice("Server version: \(plist.version, privacy: .public)")
logger.notice("Server version: \(plist.version, privacy: .public), build: \(plist.build, privacy: .public)")

let listener = NSXPCListener(machServiceName: Constant.helperBundleIdentifier)
let delegate = ListenerDelegate()
Expand All @@ -36,6 +36,8 @@ public final class Server {

options.tracesSampleRate = 1.0
options.diagnosticLevel = .warning

options.releaseName = "BatFiHelper@\(plist.version)@\(plist.build)"
}
}

Expand Down
6 changes: 4 additions & 2 deletions Helper/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
<dict>
<key>CFBundleIdentifier</key>
<string>software.micropixels.BatFi.Helper</string>
<key>CFBundleVersion</key>
<key>CFBundleShortVersionString</key>
<string>1.5.0</string>
<key>CFBundleVersion</key>
<string>50</string>
<key>SMAuthorizedClients</key>
<array>
<string>anchor apple generic and identifier "software.micropixels.BatFi" and info[CFBundleVersion] &gt;= "1.5.0" and certificate leaf[subject.OU] = "YL78V8PY6H"</string>
<string>anchor apple generic and identifier "software.micropixels.BatFi" and info[CFBundleVersion] &gt;= "50" and certificate leaf[subject.OU] = "YL78V8PY6H"</string>
</array>
</dict>
</plist>
4 changes: 2 additions & 2 deletions Supporting Files/Config.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
APP_BUNDLE_IDENTIFIER = software.micropixels.BatFi
HELPER_TOOL_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).Helper

// The app's version needs to be defined at the project level so the helper tool can encode this as a code requirement
// mitigate downgrade attacks. See README for details.
APP_VERSION = 1.5.0

BUILD_NUMBER = 50

HELPER_NAME = BatFiHelper
Loading

0 comments on commit 11657e3

Please sign in to comment.