OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/today_extension/lock_screen_state.h" |
| 6 |
| 7 #import <UIKit/UIKit.h> |
| 8 |
| 9 #import "base/ios/weak_nsobject.h" |
| 10 |
| 11 namespace { |
| 12 |
| 13 // Name of the protected file used to detect if extension is triggered on lock |
| 14 // screen. |
| 15 NSString* const kLockScreenFileName = @"LockScreenDetector"; |
| 16 |
| 17 // Return the path of a file that will always have NSFileProtectionComplete |
| 18 // protection option. This file cannot be accessed from the lock screen and can |
| 19 // be used to detect if the device is locked. |
| 20 NSString* LockedDeviceFilename() { |
| 21 NSString* path = [NSSearchPathForDirectoriesInDomains( |
| 22 NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]; |
| 23 return [path stringByAppendingPathComponent:kLockScreenFileName]; |
| 24 } |
| 25 |
| 26 } // namespace |
| 27 |
| 28 @interface LockScreenState () |
| 29 |
| 30 - (instancetype)init; |
| 31 |
| 32 // Returns if the protected file is accessible. |
| 33 // Note: the function tests the access to protected file. Access is revoked 10 |
| 34 // seconds after screen is locked. So this method returns YES if screen has been |
| 35 // locked for more than 10 seconds. |
| 36 // Returns NO if CreateLockScreenDetector has never been called. |
| 37 - (BOOL)isProtectedFileAccessible; |
| 38 |
| 39 // Periodically checks (every second) if the protected file is still accessible. |
| 40 // Checks |remainingTests| times. |
| 41 - (void)recheckProtectedFile:(int)remainingTests; |
| 42 |
| 43 // Creates a protected file that will be used to detect if screen is locked. |
| 44 - (void)createLockScreenDetector; |
| 45 |
| 46 @end |
| 47 |
| 48 @implementation LockScreenState { |
| 49 id<LockScreenStateDelegate> _delegate; |
| 50 BOOL _screenLocked; |
| 51 BOOL _lockScreenStateKnown; |
| 52 BOOL _protectedFileCreated; |
| 53 } |
| 54 |
| 55 - (instancetype)init { |
| 56 self = [super init]; |
| 57 if (self) { |
| 58 [[NSNotificationCenter defaultCenter] |
| 59 addObserver:self |
| 60 selector:@selector(screenLocked:) |
| 61 name:UIApplicationProtectedDataWillBecomeUnavailable |
| 62 object:nil]; |
| 63 [[NSNotificationCenter defaultCenter] |
| 64 addObserver:self |
| 65 selector:@selector(screenUnlocked:) |
| 66 name:UIApplicationProtectedDataDidBecomeAvailable |
| 67 object:nil]; |
| 68 _delegate = nil; |
| 69 } |
| 70 return self; |
| 71 } |
| 72 |
| 73 + (instancetype)sharedInstance { |
| 74 static LockScreenState* instance = [[LockScreenState alloc] init]; |
| 75 return instance; |
| 76 } |
| 77 |
| 78 - (void)screenLocked:(id)arguments { |
| 79 _screenLocked = YES; |
| 80 _lockScreenStateKnown = YES; |
| 81 [_delegate lockScreenStateDidChange:self]; |
| 82 } |
| 83 |
| 84 - (void)screenUnlocked:(id)arguments { |
| 85 _screenLocked = NO; |
| 86 _lockScreenStateKnown = YES; |
| 87 [_delegate lockScreenStateDidChange:self]; |
| 88 } |
| 89 |
| 90 - (void)setDelegate:(id)delegate { |
| 91 _delegate = delegate; |
| 92 } |
| 93 |
| 94 - (BOOL)isScreenLocked { |
| 95 if (_lockScreenStateKnown) { |
| 96 return _screenLocked; |
| 97 } |
| 98 [self createLockScreenDetector]; |
| 99 if (!_protectedFileCreated) { |
| 100 // File could not be created. Screen is locked. |
| 101 _screenLocked = YES; |
| 102 _lockScreenStateKnown = YES; |
| 103 return YES; |
| 104 } |
| 105 _screenLocked = ![self isProtectedFileAccessible]; |
| 106 _lockScreenStateKnown = YES; |
| 107 |
| 108 if (!_screenLocked) { |
| 109 // This test is only valid if screen has been locked for more than 10 |
| 110 // seconds (https://www.apple.com/business/docs/iOS_Security_Guide.pdf). |
| 111 // In that case, no notification is received after the delay. |
| 112 // Reschedule tests every second for 15 seconds. |
| 113 base::WeakNSObject<LockScreenState> weakSelf(self); |
| 114 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, |
| 115 static_cast<int64_t>(1 * NSEC_PER_SEC)), |
| 116 dispatch_get_main_queue(), ^{ |
| 117 [weakSelf recheckProtectedFile:14]; |
| 118 }); |
| 119 } |
| 120 return _screenLocked; |
| 121 } |
| 122 |
| 123 - (void)recheckProtectedFile:(int)remainingTests { |
| 124 BOOL screenLocked = ![self isProtectedFileAccessible]; |
| 125 if (screenLocked) { |
| 126 // |_screenLocked| may already be set to YES if we received a notification. |
| 127 if (!_screenLocked) { |
| 128 _screenLocked = YES; |
| 129 [_delegate lockScreenStateDidChange:self]; |
| 130 } |
| 131 return; |
| 132 } |
| 133 if (remainingTests) { |
| 134 base::WeakNSObject<LockScreenState> weakSelf(self); |
| 135 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, |
| 136 static_cast<int64_t>(1 * NSEC_PER_SEC)), |
| 137 dispatch_get_main_queue(), ^{ |
| 138 [weakSelf recheckProtectedFile:remainingTests - 1]; |
| 139 }); |
| 140 } |
| 141 } |
| 142 |
| 143 - (BOOL)isProtectedFileAccessible { |
| 144 NSError* error = nil; |
| 145 if (![[NSFileManager defaultManager] |
| 146 fileExistsAtPath:LockedDeviceFilename()]) { |
| 147 return NO; |
| 148 } |
| 149 [NSData dataWithContentsOfFile:LockedDeviceFilename() |
| 150 options:NSDataReadingMappedIfSafe |
| 151 error:&error]; |
| 152 return !error; |
| 153 } |
| 154 |
| 155 - (void)createLockScreenDetector { |
| 156 if (_protectedFileCreated) |
| 157 return; |
| 158 NSError* error = nil; |
| 159 if ([[NSFileManager defaultManager] |
| 160 fileExistsAtPath:LockedDeviceFilename()]) { |
| 161 NSDictionary* attributes = [[NSFileManager defaultManager] |
| 162 attributesOfItemAtPath:LockedDeviceFilename() |
| 163 error:&error]; |
| 164 if ([[attributes valueForKey:NSFileProtectionKey] |
| 165 isEqualToString:NSFileProtectionComplete]) { |
| 166 _protectedFileCreated = YES; |
| 167 return; |
| 168 } |
| 169 BOOL removed = |
| 170 [[NSFileManager defaultManager] removeItemAtPath:LockedDeviceFilename() |
| 171 error:&error]; |
| 172 if (!removed || error) { |
| 173 // File could not be removed. FileSystem may be locked ? |
| 174 return; |
| 175 } |
| 176 } |
| 177 BOOL created = [[NSFileManager defaultManager] |
| 178 createFileAtPath:LockedDeviceFilename() |
| 179 contents:[@"Unlock" dataUsingEncoding:NSUTF8StringEncoding] |
| 180 attributes:@{NSFileProtectionKey : NSFileProtectionComplete}]; |
| 181 if (!created) { |
| 182 // File could not be created. FileSystem may be locked ? |
| 183 return; |
| 184 } |
| 185 NSDictionary* attributes = [[NSFileManager defaultManager] |
| 186 attributesOfItemAtPath:LockedDeviceFilename() |
| 187 error:&error]; |
| 188 if ([[attributes valueForKey:NSFileProtectionKey] |
| 189 isEqualToString:NSFileProtectionComplete]) { |
| 190 _protectedFileCreated = YES; |
| 191 return; |
| 192 } |
| 193 [[NSFileManager defaultManager] removeItemAtPath:LockedDeviceFilename() |
| 194 error:&error]; |
| 195 } |
| 196 |
| 197 @end |
OLD | NEW |