1 /* Cydia Substrate - Powerful Code Insertion Platform
2 * Copyright (C) 2008-2012 Jay Freeman (saurik)
5 /* GNU Lesser General Public License, Version 3 {{{ */
7 * Substrate is free software: you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
12 * Substrate is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with Substrate. If not, see <http://www.gnu.org/licenses/>.
22 %apt Package: com.saurik.substrate.safemode
23 %apt Author: Jay Freeman (saurik) <saurik@saurik.com>
25 %apt Name: Substrate Safe Mode
26 %apt Description: safe mode safety extension (safe)
28 %apt Depends: mobilesubstrate (>= 0.9.3367+38)
33 %bundle com.apple.springboard
35 %flag -framework Foundation
36 %flag -framework UIKit
38 #import <CoreFoundation/CoreFoundation.h>
39 #import <Foundation/Foundation.h>
40 #import <CoreGraphics/CGGeometry.h>
41 #import <UIKit/UIKit.h>
43 #include "CydiaSubstrate.h"
45 MSClassHook(UIStatusBar)
48 MSMetaClassHook(UIImage)
50 MSClassHook(AAAccountManager)
51 MSMetaClassHook(AAAccountManager)
53 MSClassHook(BBSectionInfo)
54 MSClassHook(BKSApplicationLaunchSettings)
56 MSClassHook(SBAlertItemsController)
57 MSClassHook(SBButtonBar)
58 MSClassHook(SBIconController)
59 MSClassHook(SBStatusBar)
60 MSClassHook(SBStatusBarDataManager)
61 MSClassHook(SBStatusBarTimeView)
62 MSClassHook(SBUIController)
64 Class $SafeModeAlertItem;
66 @interface SBAlertItem : NSObject {
68 - (UIAlertView *) alertSheet;
72 @interface SBAlertItemsController : NSObject {
74 + (SBAlertItemsController *) sharedInstance;
75 - (void) activateAlertItem:(SBAlertItem *)item;
78 @interface SBStatusBarTimeView : UIView {
83 @interface UIApplication (CydiaSubstrate)
84 - (void) applicationOpenURL:(id)url;
87 @interface UIAlertView (CydiaSubstrate)
88 - (void) setForceHorizontalButtonsLayout:(BOOL)force;
89 - (void) setBodyText:(NSString *)body;
90 - (void) setNumberOfRows:(NSInteger)rows;
93 void SafeModeAlertItem$alertSheet$buttonClicked$(id self, SEL sel, id sheet, int button) {
99 if (kCFCoreFoundationVersionNumber >= 700)
100 system("killall backboardd");
102 // XXX: there are better ways of restarting SpringBoard that would actually save state
107 [[UIApplication sharedApplication] applicationOpenURL:[NSURL URLWithString:@"http://cydia.saurik.com/safemode/"]];
114 void SafeModeAlertItem$configure$requirePasscodeForActions$(id self, SEL sel, BOOL configure, BOOL require) {
115 UIAlertView *sheet([self alertSheet]);
117 [sheet setDelegate:self];
118 [sheet setBodyText:@"We apologize for the inconvenience, but SpringBoard has just crashed.\n\nMobileSubstrate /did not/ cause this problem: it has protected you from it.\n\nYour device is now running in Safe Mode. All extensions that support this safety system are disabled.\n\nReboot (or restart SpringBoard) to return to the normal mode. To return to this dialog touch the status bar.\n\nTap \"Help\" below for more tips."];
119 [sheet addButtonWithTitle:@"OK"];
120 [sheet addButtonWithTitle:@"Restart"];
121 [sheet addButtonWithTitle:@"Help"];
122 [sheet setNumberOfRows:1];
124 if ([sheet respondsToSelector:@selector(setForceHorizontalButtonsLayout:)])
125 [sheet setForceHorizontalButtonsLayout:YES];
128 void SafeModeAlertItem$performUnlockAction(id self, SEL sel) {
129 [[$SBAlertItemsController sharedInstance] activateAlertItem:self];
132 static void MSAlert() {
133 if ($SafeModeAlertItem == nil)
134 $SafeModeAlertItem = objc_lookUpClass("SafeModeAlertItem");
135 if ($SafeModeAlertItem == nil) {
136 $SafeModeAlertItem = objc_allocateClassPair(objc_getClass("SBAlertItem"), "SafeModeAlertItem", 0);
137 if ($SafeModeAlertItem == nil)
140 class_addMethod($SafeModeAlertItem, @selector(alertSheet:buttonClicked:), (IMP) &SafeModeAlertItem$alertSheet$buttonClicked$, "v@:@i");
141 class_addMethod($SafeModeAlertItem, @selector(configure:requirePasscodeForActions:), (IMP) &SafeModeAlertItem$configure$requirePasscodeForActions$, "v@:cc");
142 class_addMethod($SafeModeAlertItem, @selector(performUnlockAction), (IMP) SafeModeAlertItem$performUnlockAction, "v@:");
143 objc_registerClassPair($SafeModeAlertItem);
146 if ($SBAlertItemsController != nil)
147 [[$SBAlertItemsController sharedInstance] activateAlertItem:[[[$SafeModeAlertItem alloc] init] autorelease]];
151 // XXX: on iOS 5.0, we really would prefer avoiding
153 MSInstanceMessageHook2(void, SBStatusBar, touchesEnded,withEvent, id, touches, id, event) {
155 MSOldCall(touches, event);
158 MSInstanceMessageHook1(void, SBStatusBar, mouseDown, void *, event) {
163 MSInstanceMessageHook2(void, UIStatusBar, touchesBegan,withEvent, void *, touches, void *, event) {
165 MSOldCall(touches, event);
169 // this fairly complex code came from Grant, to solve the "it Safe Mode"-in-bar bug
171 MSInstanceMessageHook0(void, SBStatusBarDataManager, _updateTimeString) {
172 char *_data(&MSHookIvar<char>(self, "_data"));
176 Ivar _itemIsEnabled(object_getInstanceVariable(self, "_itemIsEnabled", NULL));
177 if (_itemIsEnabled == NULL)
180 Ivar _itemIsCloaked(object_getInstanceVariable(self, "_itemIsCloaked", NULL));
181 if (_itemIsCloaked == NULL)
184 size_t enabledOffset(ivar_getOffset(_itemIsEnabled));
185 size_t cloakedOffset(ivar_getOffset(_itemIsCloaked));
186 if (enabledOffset >= cloakedOffset)
189 size_t offset(cloakedOffset - enabledOffset);
190 char *timeString(_data + offset);
191 strcpy(timeString, "Exit Safe Mode");
195 static bool alerted_;
197 static void AlertIfNeeded() {
205 // on iOS 4.3 and above we can use this advertisement, which seems to check every time the user unlocks
206 // XXX: verify that this still works on iOS 5.0
208 MSClassMessageHook0(void, AAAccountManager, showMobileMeOfferIfNecessary) {
213 // -[SBIconController showInfoAlertIfNeeded] explains how to drag icons around the iPhone home screen
214 // it used to be shown to users when they unlocked their screen for the first time, and happened every unlock
215 // however, as of iOS 4.3, it got relegated to only appearing once the user installed an app or web clip
217 MSInstanceMessageHook0(void, SBIconController, showInfoAlertIfNeeded) {
222 // the icon state, including crazy configurations like Five Icon Dock, is stored in SpringBoard's defaults
223 // unfortunately, SpringBoard on iOS 2.0 and 2.1 (maybe 2.2 as well) buffer overrun with more than 4 icons
224 // there is a third party package called IconSupport that remedies this, but not everyone is using it yet
226 MSInstanceMessageHook0(int, SBButtonBar, maxIconColumns) {
230 if (NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults])
231 if (NSDictionary *iconState = [defaults objectForKey:@"iconState"])
232 if (NSDictionary *buttonBar = [iconState objectForKey:@"buttonBar"])
233 if (NSArray *iconMatrix = [buttonBar objectForKey:@"iconMatrix"])
234 if ([iconMatrix count] != 0)
235 if (NSArray *row = [iconMatrix objectAtIndex:0]) {
236 int count([row count]);
244 MSInstanceMessageHook0(id, SBUIController, init) {
245 if ((self = MSOldCall()) != nil) {
246 UIView *&_contentLayer(MSHookIvar<UIView *>(self, "_contentLayer"));
247 UIView *&_contentView(MSHookIvar<UIView *>(self, "_contentView"));
250 if (&_contentLayer != NULL)
251 layer = _contentLayer;
252 else if (&_contentView != NULL)
253 layer = _contentView;
258 [layer setBackgroundColor:[UIColor darkGrayColor]];
262 #define Paper_ "/Library/MobileSubstrate/MobileSafety.png"
264 MSClassMessageHook0(UIImage *, UIImage, defaultDesktopImage) {
265 return [UIImage imageWithContentsOfFile:@Paper_];
268 MSInstanceMessageHook0(void, SBStatusBarTimeView, tile) {
269 NSString *&_time(MSHookIvar<NSString *>(self, "_time"));
270 CGRect &_textRect(MSHookIvar<CGRect>(self, "_textRect"));
273 _time = [@"Exit Safe Mode" retain];
274 id font([self textFont]);
275 CGSize size([_time sizeWithFont:font]);
276 CGRect frame([self frame]);
277 _textRect.size = size;
278 _textRect.origin.x = (frame.size.width - size.width) / 2;
279 _textRect.origin.y = (frame.size.height - size.height) / 2;
283 // notification widgets ("wee apps" or "bulletin board sections") are capable of crashing SpringBoard
284 // unfortunately, which ones are in use are stored in SpringBoard's defaults, so we need to turn them off
286 MSInstanceMessageHook0(BOOL, BBSectionInfo, showsInNotificationCenter) {
291 // on iOS 6.0, Apple split parts of SpringBoard into a daemon called backboardd, including app launches
292 // in order to allow safe mode to propogate into applications, we need to then tell backboardd here
293 // XXX: (all of this should be replaced, however, with per-process launchd-mediated exception handling)
295 MSInstanceMessageHook1(void, BKSApplicationLaunchSettings, setEnvironment, NSDictionary *, original) {
297 return MSOldCall(nil);
299 NSMutableDictionary *modified([original mutableCopy]);
300 [modified setObject:@"1" forKey:@"_MSSafeMode"];
301 return MSOldCall(modified);
305 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
307 // on iOS 6, backboardd is in charge of brightness, and freaks out when SpringBoard restarts :(
308 // the result is that the device is super dark until we attempt to update the brightness here.
310 if (kCFCoreFoundationVersionNumber >= 700) {
311 if (void (*GSEventSetBacklightLevel)(float) = reinterpret_cast<void (*)(float)>(dlsym(RTLD_DEFAULT, "GSEventSetBacklightLevel")))
312 if (NSMutableDictionary *defaults = [NSMutableDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Library/Preferences/com.apple.springboard.plist", NSHomeDirectory()]])
313 if (NSNumber *level = [defaults objectForKey:@"SBBacklightLevel2"])
314 GSEventSetBacklightLevel([level floatValue]);