diff --git a/devicemanagement/device_manager.py b/devicemanagement/device_manager.py index 067a368..3eb6c29 100644 --- a/devicemanagement/device_manager.py +++ b/devicemanagement/device_manager.py @@ -12,7 +12,7 @@ from pymobiledevice3.exceptions import MuxException, PasswordRequiredError from devicemanagement.constants import Device, Version from devicemanagement.data_singleton import DataSingleton -from tweaks.tweaks import tweaks, FeatureFlagTweak, EligibilityTweak, AITweak, BasicPlistTweak, AdvancedPlistTweak, RdarFixTweak +from tweaks.tweaks import tweaks, FeatureFlagTweak, EligibilityTweak, AITweak, BasicPlistTweak, AdvancedPlistTweak, RdarFixTweak, NullifyFileTweak from tweaks.custom_gestalt_tweaks import CustomGestaltTweaks from tweaks.basic_plist_locations import FileLocationsList, RiskyFileLocationsList from Sparserestore.restore import restore_files, FileToRestore @@ -208,8 +208,8 @@ class DeviceManager: QMessageBox.information(None, "Pairing Reset", "Your device's pairing was successfully reset. Refresh the device list before applying.") - def add_skip_setup(self, files_to_restore: list[FileToRestore]): - if self.skip_setup and not self.get_current_device_supported(): + def add_skip_setup(self, files_to_restore: list[FileToRestore], restoring_domains: bool): + if self.skip_setup and (not self.get_current_device_supported() or restoring_domains): # add the 2 skip setup files cloud_config_plist: dict = { "SkipSetup": ["WiFi", "Location", "Restore", "SIMSetup", "Android", "AppleID", "IntendedUser", "TOS", "Siri", "ScreenTime", "Diagnostics", "SoftwareUpdate", "Passcode", "Biometric", "Payment", "Zoom", "DisplayTone", "MessagingActivationUsingPhoneNumber", "HomeButtonSensitivity", "CloudStorage", "ScreenSaver", "TapToSetup", "Keyboard", "PreferredLanguage", "SpokenLanguage", "WatchMigration", "OnBoarding", "TVProviderSignIn", "TVHomeScreenSync", "Privacy", "TVRoom", "iMessageAndFaceTime", "AppStore", "Safety", "Multitasking", "ActionButton", "TermsOfAddress", "AccessibilityAppearance", "Welcome", "Appearance", "RestoreCompleted", "UpdateCompleted"], @@ -241,7 +241,7 @@ class DeviceManager: def get_domain_for_path(self, path: str) -> str: # returns Domain: str?, Path: str - if self.get_current_device_supported(): + if self.get_current_device_supported() and not path.startswith("/var/mobile/"): # don't do anything on sparserestore versions return None, path fully_patched = self.get_current_device_patched() @@ -274,20 +274,13 @@ class DeviceManager: return None, path def concat_file(self, contents: str, path: str, files_to_restore: list[FileToRestore], owner: int = 501, group: int = 501): - if self.get_current_device_supported(): - files_to_restore.append(FileToRestore( - contents=contents, - restore_path=path, - owner=owner, group=group - )) - else: - domain, file_path = self.get_domain_for_path(path) - files_to_restore.append(FileToRestore( - contents=contents, - restore_path=file_path, - domain=domain, - owner=owner, group=group - )) + domain, file_path = self.get_domain_for_path(path) + files_to_restore.append(FileToRestore( + contents=contents, + restore_path=file_path, + domain=domain, + owner=owner, group=group + )) ## APPLYING OR REMOVING TWEAKS AND RESTORING def apply_changes(self, resetting: bool = False, update_label=lambda x: None): @@ -304,6 +297,8 @@ class DeviceManager: ai_file = None basic_plists: dict = {} basic_plists_ownership: dict = {} + files_data: dict = {} + uses_domains: bool = False # set the plist keys if not resetting: @@ -318,6 +313,10 @@ class DeviceManager: elif isinstance(tweak, BasicPlistTweak) or isinstance(tweak, RdarFixTweak) or isinstance(tweak, AdvancedPlistTweak): basic_plists = tweak.apply_tweak(basic_plists, self.allow_risky_tweaks) basic_plists_ownership[tweak.file_location] = tweak.owner + elif isinstance(tweak, NullifyFileTweak): + tweak.apply_tweak(files_data) + if tweak.enabled and tweak.file_location.value.startswith("/var/mobile/"): + uses_domains = True else: if gestalt_plist != None: gestalt_plist = tweak.apply_tweak(gestalt_plist) @@ -341,7 +340,7 @@ class DeviceManager: path="/var/preferences/FeatureFlags/Global.plist", files_to_restore=files_to_restore ) - self.add_skip_setup(files_to_restore) + self.add_skip_setup(files_to_restore, uses_domains) if gestalt_data != None: self.concat_file( contents=gestalt_data, @@ -375,6 +374,13 @@ class DeviceManager: files_to_restore=files_to_restore, owner=ownership, group=ownership ) + for location, data in files_data.items(): + self.concat_file( + contents=data, + path=location.value, + files_to_restore=files_to_restore, + owner=ownership, group=ownership + ) # reset basic tweaks if resetting: empty_data = plistlib.dumps({}) diff --git a/gui/main_window.py b/gui/main_window.py index ac7f073..e306d31 100644 --- a/gui/main_window.py +++ b/gui/main_window.py @@ -135,6 +135,7 @@ class MainWindow(QtWidgets.QMainWindow): self.ui.usageTrackingAgentChk.toggled.connect(self.on_usageTrackingAgentChk_clicked) self.ui.gameCenterChk.toggled.connect(self.on_gameCenterChk_clicked) self.ui.screenTimeChk.toggled.connect(self.on_screenTimeChk_clicked) + self.ui.clearScreenTimeAgentChk.toggled.connect(self.on_clearScreenTimeAgentChk_clicked) self.ui.crashReportsChk.toggled.connect(self.on_crashReportsChk_clicked) self.ui.atwakeupChk.toggled.connect(self.on_atwakeupChk_clicked) self.ui.tipsChk.toggled.connect(self.on_tipsChk_clicked) @@ -760,6 +761,8 @@ class MainWindow(QtWidgets.QMainWindow): "com.apple.homed", "com.apple.familycircled" ], value=checked) + def on_clearScreenTimeAgentChk_clicked(self, checked: bool): + tweaks["ClearScreenTimeAgentPlist"].set_enabled(checked) def on_crashReportsChk_clicked(self, checked: bool): tweaks["Daemons"].set_multiple_values([ "com.apple.ReportCrash", diff --git a/qt/mainwindow.ui b/qt/mainwindow.ui index fcb1e3f..5a30af8 100644 --- a/qt/mainwindow.ui +++ b/qt/mainwindow.ui @@ -3797,6 +3797,18 @@ QComboBox QAbstractItemView::item:hover { + + + + Deletes the Screen Time Agent preferences file to prevent app lockout set via iCloud. + +To work properly, also disable the daemon using the toggle above. + + + Clear ScreenTimeAgent.plist file + + + diff --git a/qt/mainwindow_ui.py b/qt/mainwindow_ui.py index 5c68f9c..6fbd52d 100644 --- a/qt/mainwindow_ui.py +++ b/qt/mainwindow_ui.py @@ -1993,6 +1993,11 @@ class Ui_Nugget(object): self.verticalLayout_132.addWidget(self.screenTimeChk) + self.clearScreenTimeAgentChk = QCheckBox(self.daemonsPageContent) + self.clearScreenTimeAgentChk.setObjectName(u"clearScreenTimeAgentChk") + + self.verticalLayout_132.addWidget(self.clearScreenTimeAgentChk) + self.crashReportsChk = QCheckBox(self.daemonsPageContent) self.crashReportsChk.setObjectName(u"crashReportsChk") @@ -3319,6 +3324,12 @@ class Ui_Nugget(object): self.screenTimeChk.setToolTip(QCoreApplication.translate("Nugget", u"Disables Screen Time monitoring features.", None)) #endif // QT_CONFIG(tooltip) self.screenTimeChk.setText(QCoreApplication.translate("Nugget", u"Disable Screen Time Agent", None)) +#if QT_CONFIG(tooltip) + self.clearScreenTimeAgentChk.setToolTip(QCoreApplication.translate("Nugget", u"Deletes the Screen Time Agent preferences file to prevent app lockout set via iCloud.\n" +"\n" +"To work properly, also disable the daemon using the toggle above.", None)) +#endif // QT_CONFIG(tooltip) + self.clearScreenTimeAgentChk.setText(QCoreApplication.translate("Nugget", u"Clear ScreenTimeAgent.plist file", None)) #if QT_CONFIG(tooltip) self.crashReportsChk.setToolTip(QCoreApplication.translate("Nugget", u"Stops logs, dumps, and crash reports collection.", None)) #endif // QT_CONFIG(tooltip) diff --git a/qt/ui_mainwindow.py b/qt/ui_mainwindow.py index a06957d..681d2d7 100644 --- a/qt/ui_mainwindow.py +++ b/qt/ui_mainwindow.py @@ -1993,6 +1993,11 @@ class Ui_Nugget(object): self.verticalLayout_132.addWidget(self.screenTimeChk) + self.clearScreenTimeAgentChk = QCheckBox(self.daemonsPageContent) + self.clearScreenTimeAgentChk.setObjectName(u"clearScreenTimeAgentChk") + + self.verticalLayout_132.addWidget(self.clearScreenTimeAgentChk) + self.crashReportsChk = QCheckBox(self.daemonsPageContent) self.crashReportsChk.setObjectName(u"crashReportsChk") @@ -3319,6 +3324,12 @@ class Ui_Nugget(object): self.screenTimeChk.setToolTip(QCoreApplication.translate("Nugget", u"Disables Screen Time monitoring features.", None)) #endif // QT_CONFIG(tooltip) self.screenTimeChk.setText(QCoreApplication.translate("Nugget", u"Disable Screen Time Agent", None)) +#if QT_CONFIG(tooltip) + self.clearScreenTimeAgentChk.setToolTip(QCoreApplication.translate("Nugget", u"Deletes the Screen Time Agent preferences file to prevent app lockout set via iCloud.\n" +"\n" +"To work properly, also disable the daemon using the toggle above.", None)) +#endif // QT_CONFIG(tooltip) + self.clearScreenTimeAgentChk.setText(QCoreApplication.translate("Nugget", u"Clear ScreenTimeAgent.plist file", None)) #if QT_CONFIG(tooltip) self.crashReportsChk.setToolTip(QCoreApplication.translate("Nugget", u"Stops logs, dumps, and crash reports collection.", None)) #endif // QT_CONFIG(tooltip) diff --git a/tweaks/basic_plist_locations.py b/tweaks/basic_plist_locations.py index c95c9e0..43aef7b 100644 --- a/tweaks/basic_plist_locations.py +++ b/tweaks/basic_plist_locations.py @@ -18,6 +18,7 @@ class FileLocation(Enum): # Daemons disabledDaemons = "/var/db/com.apple.xpc.launchd/disabled.plist" + screentime = "/var/mobile/Library/Preferences/ScreenTimeAgent.plist" # Risky Options ota = "/var/Managed Preferences/mobile/com.apple.MobileAsset.plist" diff --git a/tweaks/tweak_classes.py b/tweaks/tweak_classes.py index e5182ff..15b6a61 100644 --- a/tweaks/tweak_classes.py +++ b/tweaks/tweak_classes.py @@ -43,6 +43,21 @@ class Tweak: def apply_tweak(self): raise NotImplementedError +class NullifyFileTweak(Tweak): + def __init__( + self, label: str, + file_location: FileLocation, + min_version: Version = Version("1.0"), + owner: int = 501, group: int = 501, + divider_below: bool = False + ): + super().__init__(label=label, key=None, value=None, min_version=min_version, owner=owner, group=group, divider_below=divider_below) + self.file_location = file_location + + def apply_tweak(self, other_tweaks: dict): + if self.enabled: + other_tweaks[self.file_location] = b"" + class BasicPlistTweak(Tweak): def __init__( diff --git a/tweaks/tweaks.py b/tweaks/tweaks.py index ee435f8..0a07870 100644 --- a/tweaks/tweaks.py +++ b/tweaks/tweaks.py @@ -1,5 +1,5 @@ from devicemanagement.constants import Version -from .tweak_classes import MobileGestaltTweak, MobileGestaltMultiTweak, MobileGestaltPickerTweak, FeatureFlagTweak, TweakModifyType, BasicPlistTweak, AdvancedPlistTweak, RdarFixTweak +from .tweak_classes import MobileGestaltTweak, MobileGestaltMultiTweak, MobileGestaltPickerTweak, FeatureFlagTweak, TweakModifyType, BasicPlistTweak, AdvancedPlistTweak, RdarFixTweak, NullifyFileTweak from .eligibility_tweak import EligibilityTweak, AITweak from .basic_plist_locations import FileLocation @@ -283,6 +283,9 @@ tweaks = { }, owner=0, group=0 ), + "ClearScreenTimeAgentPlist": NullifyFileTweak( + "Clear ScreenTimeAgent Plist", FileLocation.screentime + ), ## Risky Options "DisableOTAFile": AdvancedPlistTweak(