294 lines
9.7 KiB
Swift
294 lines
9.7 KiB
Swift
//
|
|
// Helper.swift
|
|
// AlDente
|
|
//
|
|
// Created by David Wernhart on 14.02.20.
|
|
// Copyright © 2020 David Wernhart. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import ServiceManagement
|
|
import IOKit.pwr_mgt
|
|
|
|
protocol HelperDelegate {
|
|
func OnMaxBatRead(value: UInt8)
|
|
func updateStatus(status:String)
|
|
}
|
|
|
|
final class Helper {
|
|
|
|
static let instance = Helper()
|
|
|
|
public var delegate: HelperDelegate?
|
|
|
|
private var key: String?
|
|
|
|
private var preventSleepID: IOPMAssertionID?
|
|
|
|
public var appleSilicon:Bool?
|
|
public var chargeInhibited: Bool = false
|
|
public var isInitialized:Bool = false
|
|
|
|
public var statusString:String = ""
|
|
|
|
|
|
lazy var helperToolConnection: NSXPCConnection = {
|
|
let connection = NSXPCConnection(machServiceName: "com.davidwernhart.Helper.mach", options: .privileged)
|
|
connection.remoteObjectInterface = NSXPCInterface(with: HelperToolProtocol.self)
|
|
|
|
connection.resume()
|
|
return connection
|
|
}()
|
|
|
|
func setPlatformKey() {
|
|
let s:String! = ProcessInfo.init().machineHardwareName
|
|
if(s != nil){
|
|
if(s.elementsEqual("x86_64")){
|
|
print("intel cpu!")
|
|
appleSilicon = false;
|
|
}
|
|
else if(s.elementsEqual("arm64")){
|
|
print("arm cpu!")
|
|
appleSilicon = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
func setStatusString(){
|
|
checkCharging()
|
|
var sleepDisabled:Bool = !(preventSleepID == nil)
|
|
statusString = ""
|
|
if(PersistanceManager.instance.oldKey){
|
|
statusString = "BCLM Key Mode. Final charge value can differ by up to 5%"
|
|
}
|
|
else{
|
|
statusString = "Charge Inhibit: "+String(chargeInhibited)+" | Prevent Sleep: "+String(sleepDisabled)+" | Helper v"+String(helperVersion)+": \(self.isInitialized ? "found" : "not found")"
|
|
}
|
|
|
|
|
|
self.delegate?.updateStatus(status: statusString)
|
|
}
|
|
|
|
|
|
func enableSleep(){
|
|
if(self.preventSleepID != nil){
|
|
print("RELEASING PREVENT SLEEP ASSERTION WITH ID: ",preventSleepID!)
|
|
releaseAssertion(assertionId: self.preventSleepID!)
|
|
self.preventSleepID = nil
|
|
}
|
|
}
|
|
|
|
func disableSleep(){
|
|
createAssertion(assertion: kIOPMAssertionTypePreventSystemSleep){ id in
|
|
if(self.preventSleepID == nil){
|
|
print("PREVENT SLEEP ASSERTION CREATED! ID: ",id)
|
|
self.preventSleepID = id
|
|
}
|
|
}
|
|
}
|
|
|
|
func enableCharging(){
|
|
if(appleSilicon!){
|
|
SMCWriteByte(key: "CH0B", value: 00)
|
|
}
|
|
SMCWriteByte(key: "CH0B", value: 00)
|
|
self.chargeInhibited = false
|
|
|
|
}
|
|
|
|
func disableCharging(){
|
|
if(appleSilicon!){
|
|
SMCWriteByte(key: "CH0B", value: 02)
|
|
}
|
|
SMCWriteByte(key: "CH0B", value: 02)
|
|
self.chargeInhibited = true
|
|
|
|
}
|
|
|
|
func checkCharging(){
|
|
Helper.instance.SMCReadUInt32(key: "CH0B") { value in
|
|
self.chargeInhibited = !(value == 00)
|
|
print("CHARGE INHIBITED: "+String(self.chargeInhibited))
|
|
}
|
|
if(PersistanceManager.instance.oldKey){
|
|
Helper.instance.readMaxBatteryCharge()
|
|
}
|
|
|
|
}
|
|
|
|
func getChargingInfo(withReply reply: (String,Int,Bool,Int) -> Void){
|
|
let snapshot = IOPSCopyPowerSourcesInfo().takeRetainedValue()
|
|
let sources = IOPSCopyPowerSourcesList(snapshot).takeRetainedValue() as Array
|
|
let info = IOPSGetPowerSourceDescription(snapshot, sources[0]).takeUnretainedValue() as! [String: AnyObject]
|
|
|
|
if let name = info[kIOPSNameKey] as? String,
|
|
let capacity = info[kIOPSCurrentCapacityKey] as? Int,
|
|
let isCharging = info[kIOPSIsChargingKey] as? Bool,
|
|
let max = info[kIOPSMaxCapacityKey] as? Int {
|
|
reply(name,capacity,isCharging,max)
|
|
}
|
|
}
|
|
|
|
func getSMCCharge(withReply reply: @escaping (Float)->Void){
|
|
Helper.instance.SMCReadUInt32(key: "BRSC") { value in
|
|
let smcval = Float(value >> 16)
|
|
reply(smcval)
|
|
}
|
|
}
|
|
|
|
@objc func createAssertion(assertion: String, withReply reply: @escaping (IOPMAssertionID) -> Void){
|
|
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
|
let e = $0 as NSError
|
|
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
|
} as? HelperToolProtocol
|
|
|
|
helper?.createAssertion(assertion: assertion, withReply: { id in
|
|
reply(id)
|
|
})
|
|
}
|
|
|
|
@objc func releaseAssertion(assertionId: IOPMAssertionID){
|
|
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
|
let e = $0 as NSError
|
|
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
|
} as? HelperToolProtocol
|
|
|
|
helper?.releaseAssertion(assertionID: assertionId)
|
|
}
|
|
|
|
@objc func installHelper() {
|
|
print("trying to install helper!")
|
|
var status = noErr
|
|
let helperID = "com.davidwernhart.Helper" as CFString // Prefs.helperID as CFString
|
|
|
|
var authItem = kSMRightBlessPrivilegedHelper.withCString {
|
|
AuthorizationItem(name: $0, valueLength: 0, value: nil, flags: 0)
|
|
}
|
|
var authRights = withUnsafeMutablePointer(to: &authItem) {
|
|
AuthorizationRights(count: 1, items: $0)
|
|
}
|
|
let authFlags: AuthorizationFlags = [.interactionAllowed, .preAuthorize, .extendRights]
|
|
var authRef: AuthorizationRef?
|
|
status = AuthorizationCreate(&authRights, nil, authFlags, &authRef)
|
|
if status != errAuthorizationSuccess {
|
|
print(SecCopyErrorMessageString(status, nil) ?? "")
|
|
print("Error: \(status)")
|
|
}
|
|
|
|
var error: Unmanaged<CFError>?
|
|
SMJobBless(kSMDomainSystemLaunchd, helperID, authRef, &error)
|
|
if let e = error?.takeRetainedValue() {
|
|
print("Domain: ", CFErrorGetDomain(e) ?? "")
|
|
print("Code: ", CFErrorGetCode(e))
|
|
print("UserInfo: ", CFErrorCopyUserInfo(e) ?? "")
|
|
print("Description: ", CFErrorCopyDescription(e) ?? "")
|
|
print("Reason: ", CFErrorCopyFailureReason(e) ?? "")
|
|
print("Suggestion: ", CFErrorCopyRecoverySuggestion(e) ?? "")
|
|
}
|
|
|
|
if(error == nil){
|
|
print("helper installed successfully!")
|
|
restart()
|
|
}
|
|
}
|
|
|
|
func restart(){
|
|
let url = URL(fileURLWithPath: Bundle.main.resourcePath!)
|
|
let path = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString
|
|
let task = Process()
|
|
task.launchPath = "/usr/bin/open"
|
|
task.arguments = [path]
|
|
task.launch()
|
|
exit(0)
|
|
}
|
|
|
|
@objc func setResetValues(){
|
|
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
|
let e = $0 as NSError
|
|
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
|
|
|
} as? HelperToolProtocol
|
|
|
|
helper?.setResetVal(key: "CH0B", value: 00)
|
|
}
|
|
|
|
@objc func writeMaxBatteryCharge(setVal: UInt8) {
|
|
SMCWriteByte(key: "BCLM", value: setVal)
|
|
|
|
}
|
|
|
|
@objc func readMaxBatteryCharge() {
|
|
SMCReadByte(key: "BCLM") { value in
|
|
print("OLD KEY MAX CHARGE: "+String(value))
|
|
self.delegate?.OnMaxBatRead(value: value)
|
|
}
|
|
}
|
|
|
|
@objc func enableCharging(enabled: Bool) {
|
|
if(enabled){
|
|
SMCWriteByte(key: "CH0B", value: 00)
|
|
}
|
|
else{
|
|
SMCWriteByte(key: "CH0B", value: 02)
|
|
}
|
|
|
|
}
|
|
|
|
@objc func checkHelperVersion(withReply reply: @escaping (Bool) -> Void) {
|
|
print("checking helper version")
|
|
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
|
let e = $0 as NSError
|
|
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
|
reply(false)
|
|
return()
|
|
|
|
} as? HelperToolProtocol
|
|
|
|
helper?.getVersion { version in
|
|
print("helperVersion:", helperVersion, " version from helper:", version)
|
|
if !helperVersion.elementsEqual(version) {
|
|
reply(false)
|
|
return() }
|
|
else{
|
|
self.isInitialized = true
|
|
reply(true)
|
|
return()
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc func SMCReadByte(key: String, withReply reply: @escaping (UInt8) -> Void) {
|
|
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
|
let e = $0 as NSError
|
|
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
|
|
|
} as? HelperToolProtocol
|
|
|
|
helper?.readSMCByte(key: key) {
|
|
reply($0)
|
|
}
|
|
}
|
|
|
|
@objc func SMCReadUInt32(key: String, withReply reply: @escaping (UInt32) -> Void) {
|
|
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
|
let e = $0 as NSError
|
|
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
|
|
|
} as? HelperToolProtocol
|
|
|
|
helper?.readSMCUInt32(key: key) {
|
|
reply($0)
|
|
}
|
|
}
|
|
|
|
@objc func SMCWriteByte(key: String, value: UInt8) {
|
|
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
|
let e = $0 as NSError
|
|
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
|
|
|
} as? HelperToolProtocol
|
|
|
|
helper?.setSMCByte(key: key, value: value)
|
|
}
|
|
}
|