mirror of
https://github.com/bitwarden/mobile.git
synced 2024-09-27 03:52:57 +02:00
Use monotonic clock for vault timeout (#1175)
* Use monotonic clock for vault timeout * free memory * removed vault timeout timers and added crash logging to iOS clock hack
This commit is contained in:
parent
3227daddaf
commit
acf2e4360f
@ -38,7 +38,6 @@ namespace Bit.Droid
|
||||
private IAppIdService _appIdService;
|
||||
private IStorageService _storageService;
|
||||
private IEventService _eventService;
|
||||
private PendingIntent _vaultTimeoutAlarmPendingIntent;
|
||||
private PendingIntent _clearClipboardPendingIntent;
|
||||
private PendingIntent _eventUploadPendingIntent;
|
||||
private AppOptions _appOptions;
|
||||
@ -51,9 +50,6 @@ namespace Bit.Droid
|
||||
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
|
||||
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
||||
PendingIntentFlags.UpdateCurrent);
|
||||
var alarmIntent = new Intent(this, typeof(LockAlarmReceiver));
|
||||
_vaultTimeoutAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
|
||||
PendingIntentFlags.UpdateCurrent);
|
||||
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
||||
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
||||
PendingIntentFlags.UpdateCurrent);
|
||||
@ -91,20 +87,7 @@ namespace Bit.Droid
|
||||
|
||||
_broadcasterService.Subscribe(_activityKey, (message) =>
|
||||
{
|
||||
if (message.Command == "scheduleVaultTimeoutTimer")
|
||||
{
|
||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||
var vaultTimeoutMinutes = (int)message.Data;
|
||||
var vaultTimeoutMs = vaultTimeoutMinutes * 60000;
|
||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + vaultTimeoutMs + 10;
|
||||
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _vaultTimeoutAlarmPendingIntent);
|
||||
}
|
||||
else if (message.Command == "cancelVaultTimeoutTimer")
|
||||
{
|
||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||
alarmManager.Cancel(_vaultTimeoutAlarmPendingIntent);
|
||||
}
|
||||
else if (message.Command == "startEventTimer")
|
||||
if (message.Command == "startEventTimer")
|
||||
{
|
||||
StartEventAlarm();
|
||||
}
|
||||
|
@ -752,6 +752,15 @@ namespace Bit.Droid.Services
|
||||
return false;
|
||||
}
|
||||
|
||||
public long GetActiveTime()
|
||||
{
|
||||
// Returns milliseconds since the system was booted, and includes deep sleep. This clock is guaranteed to
|
||||
// be monotonic, and continues to tick even when the CPU is in power saving modes, so is the recommend
|
||||
// basis for general purpose interval timing.
|
||||
// ref: https://developer.android.com/reference/android/os/SystemClock#elapsedRealtime()
|
||||
return SystemClock.ElapsedRealtime() / 1000;
|
||||
}
|
||||
|
||||
private bool DeleteDir(Java.IO.File dir)
|
||||
{
|
||||
if (dir != null && dir.IsDirectory)
|
||||
|
@ -43,5 +43,6 @@ namespace Bit.App.Abstractions
|
||||
void OpenAccessibilityOverlayPermissionSettings();
|
||||
void OpenAutofillSettings();
|
||||
bool UsingDarkTheme();
|
||||
long GetActiveTime();
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ namespace Bit.App
|
||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||
if (!isLocked)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.LastActiveKey, DateTime.UtcNow);
|
||||
await _storageService.SaveAsync(Constants.LastActiveKey, _deviceActionService.GetActiveTime());
|
||||
}
|
||||
SetTabsPageFromAutofill(isLocked);
|
||||
await SleptAsync();
|
||||
@ -210,7 +210,7 @@ namespace Bit.App
|
||||
|
||||
private async void ResumedAsync()
|
||||
{
|
||||
_messagingService.Send("cancelVaultTimeoutTimer");
|
||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||
_messagingService.Send("startEventTimer");
|
||||
await ClearCacheIfNeededAsync();
|
||||
Prime();
|
||||
@ -302,11 +302,7 @@ namespace Bit.App
|
||||
vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
}
|
||||
vaultTimeout = vaultTimeout.GetValueOrDefault(-1);
|
||||
if (vaultTimeout > 0)
|
||||
{
|
||||
_messagingService.Send("scheduleVaultTimeoutTimer", vaultTimeout.Value);
|
||||
}
|
||||
else if (vaultTimeout == 0)
|
||||
if (vaultTimeout == 0)
|
||||
{
|
||||
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
|
||||
if (action == "logOut")
|
||||
|
@ -3,6 +3,7 @@ using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.PlatformConfiguration;
|
||||
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
||||
@ -12,6 +13,7 @@ namespace Bit.App.Pages
|
||||
public class BaseContentPage : ContentPage
|
||||
{
|
||||
private IStorageService _storageService;
|
||||
private IDeviceActionService _deviceActionService;
|
||||
|
||||
protected int ShowModalAnimationDelay = 400;
|
||||
protected int ShowPageAnimationDelay = 100;
|
||||
@ -101,18 +103,22 @@ namespace Bit.App.Pages
|
||||
});
|
||||
}
|
||||
|
||||
private void SetStorageService()
|
||||
private void SetServices()
|
||||
{
|
||||
if (_storageService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
}
|
||||
if (_deviceActionService == null)
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveActivity()
|
||||
{
|
||||
SetStorageService();
|
||||
_storageService.SaveAsync(Constants.LastActiveKey, DateTime.UtcNow);
|
||||
SetServices();
|
||||
_storageService.SaveAsync(Constants.LastActiveKey, _deviceActionService.GetActiveTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -241,5 +241,10 @@ namespace Bit.App.Services
|
||||
catch { }
|
||||
return false;
|
||||
}
|
||||
|
||||
public long GetActiveTime()
|
||||
{
|
||||
return _deviceActionService.GetActiveTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,5 +28,6 @@ namespace Bit.Core.Abstractions
|
||||
bool SupportsDuo();
|
||||
Task<bool> SupportsBiometricAsync();
|
||||
Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null, Action fallback = null);
|
||||
long GetActiveTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,13 +90,13 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return;
|
||||
}
|
||||
var lastActive = await _storageService.GetAsync<DateTime?>(Constants.LastActiveKey);
|
||||
var lastActive = await _storageService.GetAsync<long?>(Constants.LastActiveKey);
|
||||
if (lastActive == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var diff = DateTime.UtcNow - lastActive.Value;
|
||||
if (diff.TotalSeconds >= vaultTimeout.Value)
|
||||
var diff = _platformUtilsService.GetActiveTime() - lastActive;
|
||||
if (diff >= vaultTimeout * 60)
|
||||
{
|
||||
// Pivot based on saved action
|
||||
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
|
||||
|
@ -430,6 +430,13 @@ namespace Bit.iOS.Core.Services
|
||||
return false;
|
||||
}
|
||||
|
||||
public long GetActiveTime()
|
||||
{
|
||||
// Fall back to UnixTimeSeconds in case this approach stops working. We'll lose clock-change protection but
|
||||
// the lock functionality will continue to work.
|
||||
return iOSHelpers.GetSystemUpTimeSeconds() ?? DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
}
|
||||
|
||||
private void ImagePicker_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
|
||||
{
|
||||
if (sender is UIImagePickerController picker)
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Bit.App.Utilities;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Bit.App.Utilities;
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
@ -7,7 +10,52 @@ namespace Bit.iOS.Core.Utilities
|
||||
{
|
||||
public static class iOSHelpers
|
||||
{
|
||||
public static System.nfloat? GetAccessibleFont<T>(double size)
|
||||
[DllImport(ObjCRuntime.Constants.SystemLibrary)]
|
||||
internal static extern int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string property, IntPtr output,
|
||||
IntPtr oldLen, IntPtr newp, uint newLen);
|
||||
|
||||
// Returns the difference between when the system was booted and now in seconds, resulting in a duration that
|
||||
// includes sleep time.
|
||||
// ref: https://forums.xamarin.com/discussion/20006/access-to-sysctl-h
|
||||
// ref: https://github.com/XLabs/Xamarin-Forms-Labs/blob/master/src/Platform/XLabs.Platform.iOS/Device/AppleDevice.cs
|
||||
public static long? GetSystemUpTimeSeconds()
|
||||
{
|
||||
long? uptime = null;
|
||||
IntPtr pLen = default, pStr = default;
|
||||
try
|
||||
{
|
||||
var property = "kern.boottime";
|
||||
pLen = Marshal.AllocHGlobal(sizeof(int));
|
||||
sysctlbyname(property, IntPtr.Zero, pLen, IntPtr.Zero, 0);
|
||||
var length = Marshal.ReadInt32(pLen);
|
||||
pStr = Marshal.AllocHGlobal(length);
|
||||
sysctlbyname(property, pStr, pLen, IntPtr.Zero, 0);
|
||||
var timeVal = Marshal.PtrToStructure<TimeVal>(pStr);
|
||||
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
if (timeVal.sec > 0 && now > 0)
|
||||
{
|
||||
uptime = now - timeVal.sec;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Crashes.TrackError(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pLen != default)
|
||||
{
|
||||
Marshal.FreeHGlobal(pLen);
|
||||
}
|
||||
if (pStr != default)
|
||||
{
|
||||
Marshal.FreeHGlobal(pStr);
|
||||
}
|
||||
}
|
||||
return uptime;
|
||||
}
|
||||
|
||||
public static nfloat? GetAccessibleFont<T>(double size)
|
||||
{
|
||||
var pointSize = UIFontDescriptor.PreferredBody.PointSize;
|
||||
if (size == Device.GetNamedSize(NamedSize.Large, typeof(T)))
|
||||
@ -60,5 +108,11 @@ namespace Bit.iOS.Core.Utilities
|
||||
control, NSLayoutAttribute.Bottom, 1, 10f),
|
||||
});
|
||||
}
|
||||
|
||||
private struct TimeVal
|
||||
{
|
||||
public long sec;
|
||||
public long usec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ using System.Threading.Tasks;
|
||||
using AuthenticationServices;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Pages;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Services;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
@ -29,8 +28,6 @@ namespace Bit.iOS
|
||||
private Core.NFCReaderDelegate _nfcDelegate = null;
|
||||
private NSTimer _clipboardTimer = null;
|
||||
private nint _clipboardBackgroundTaskId;
|
||||
private NSTimer _vaultTimeoutTimer = null;
|
||||
private nint _lockBackgroundTaskId;
|
||||
private NSTimer _eventTimer = null;
|
||||
private nint _eventBackgroundTaskId;
|
||||
|
||||
@ -59,15 +56,7 @@ namespace Bit.iOS
|
||||
|
||||
_broadcasterService.Subscribe(nameof(AppDelegate), async (message) =>
|
||||
{
|
||||
if (message.Command == "scheduleVaultTimeoutTimer")
|
||||
{
|
||||
VaultTimeoutTimer((int)message.Data);
|
||||
}
|
||||
else if (message.Command == "cancelVaultTimeoutTimer")
|
||||
{
|
||||
CancelVaultTimeoutTimer();
|
||||
}
|
||||
else if (message.Command == "startEventTimer")
|
||||
if (message.Command == "startEventTimer")
|
||||
{
|
||||
StartEventTimer();
|
||||
}
|
||||
@ -212,7 +201,7 @@ namespace Bit.iOS
|
||||
UIApplication.SharedApplication.KeyWindow.BringSubviewToFront(view);
|
||||
UIApplication.SharedApplication.KeyWindow.EndEditing(true);
|
||||
UIApplication.SharedApplication.SetStatusBarHidden(true, false);
|
||||
_storageService.SaveAsync(Constants.LastActiveKey, DateTime.UtcNow);
|
||||
_storageService.SaveAsync(Constants.LastActiveKey, _deviceActionService.GetActiveTime());
|
||||
_messagingService.Send("slept");
|
||||
base.DidEnterBackground(uiApplication);
|
||||
}
|
||||
@ -322,55 +311,6 @@ namespace Bit.iOS
|
||||
"pushNotificationService", iosPushNotificationService);
|
||||
}
|
||||
|
||||
private void VaultTimeoutTimer(int vaultTimeoutMinutes)
|
||||
{
|
||||
if (_lockBackgroundTaskId > 0)
|
||||
{
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_lockBackgroundTaskId);
|
||||
_lockBackgroundTaskId = 0;
|
||||
}
|
||||
_lockBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
|
||||
{
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_lockBackgroundTaskId);
|
||||
_lockBackgroundTaskId = 0;
|
||||
});
|
||||
var vaultTimeoutMs = vaultTimeoutMinutes * 60000;
|
||||
_vaultTimeoutTimer?.Invalidate();
|
||||
_vaultTimeoutTimer?.Dispose();
|
||||
_vaultTimeoutTimer = null;
|
||||
var vaultTimeoutMsSpan = TimeSpan.FromMilliseconds(vaultTimeoutMs + 10);
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
_vaultTimeoutTimer = NSTimer.CreateScheduledTimer(vaultTimeoutMsSpan, timer =>
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
_vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||
_vaultTimeoutTimer?.Invalidate();
|
||||
_vaultTimeoutTimer?.Dispose();
|
||||
_vaultTimeoutTimer = null;
|
||||
if (_lockBackgroundTaskId > 0)
|
||||
{
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_lockBackgroundTaskId);
|
||||
_lockBackgroundTaskId = 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void CancelVaultTimeoutTimer()
|
||||
{
|
||||
_vaultTimeoutTimer?.Invalidate();
|
||||
_vaultTimeoutTimer?.Dispose();
|
||||
_vaultTimeoutTimer = null;
|
||||
if (_lockBackgroundTaskId > 0)
|
||||
{
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_lockBackgroundTaskId);
|
||||
_lockBackgroundTaskId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ClearClipboardTimerAsync(Tuple<string, int?, bool> data)
|
||||
{
|
||||
if (data.Item3)
|
||||
|
Loading…
Reference in New Issue
Block a user