mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-22 17:42:45 +02:00
Create managers folder
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <MatrixSDK/MatrixSDK.h>
|
||||
#import "DecryptionFailureTracker.h"
|
||||
|
||||
/**
|
||||
`Analytics` sends analytics to an analytics tool.
|
||||
*/
|
||||
@interface Analytics : NSObject <MXAnalyticsDelegate, MXDecryptionFailureDelegate>
|
||||
|
||||
/**
|
||||
Returns the shared Analytics manager.
|
||||
|
||||
@return the shared Analytics manager.
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
Start doing analytics if the settings `enableCrashReport` is enabled.
|
||||
*/
|
||||
- (void)start;
|
||||
|
||||
/**
|
||||
Stop doing analytics.
|
||||
*/
|
||||
- (void)stop;
|
||||
|
||||
/**
|
||||
Track a screen display.
|
||||
|
||||
@param screenName the name of the displayed screen.
|
||||
*/
|
||||
- (void)trackScreen:(NSString*)screenName;
|
||||
|
||||
/**
|
||||
Flush analytics data.
|
||||
*/
|
||||
- (void)dispatch;
|
||||
|
||||
/**
|
||||
Track how long the launch screen has been displayed to the end user.
|
||||
|
||||
@param seconds the duration in seconds.
|
||||
*/
|
||||
- (void)trackLaunchScreenDisplayDuration: (NSTimeInterval)seconds;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import "Analytics.h"
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
// All metrics are store under a Piwik category called "Metrics".
|
||||
// Then, there are 2 Piwik actions: "iOS.startup" and "iOS.stats" (these actions
|
||||
// are namespaced by plaform to have a nice rendering on the Piwik website).
|
||||
// Then, we use constants defined by the Matrix SDK as Piwik Names (ex:"mountData")
|
||||
NSString *const kAnalyticsMetricsCategory = @"Metrics";
|
||||
NSString *const kAnalyticsMetricsActionPattern = @"iOS.%@";
|
||||
|
||||
// E2E telemetry is stored under a Piwik category called "E2E".
|
||||
NSString *const kAnalyticsE2eCategory = @"E2E";
|
||||
NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure";
|
||||
|
||||
|
||||
@import PiwikTracker;
|
||||
|
||||
@implementation Analytics
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
{
|
||||
static Analytics *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[Analytics alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (void)start
|
||||
{
|
||||
NSDictionary *piwikConfig = [[NSUserDefaults standardUserDefaults] objectForKey:@"piwik"];
|
||||
[PiwikTracker configureSharedInstanceWithSiteID:piwikConfig[@"siteId"]
|
||||
baseURL:[NSURL URLWithString:piwikConfig[@"url"]]
|
||||
userAgent:@"iOSPiwikTracker"];
|
||||
|
||||
// Check whether the user has enabled the sending of crash reports.
|
||||
if (RiotSettings.shared.enableCrashReport)
|
||||
{
|
||||
[PiwikTracker shared].isOptedOut = NO;
|
||||
|
||||
[[PiwikTracker shared] setCustomVariableWithIndex:1 name:@"App Platform" value:@"iOS Platform"];
|
||||
[[PiwikTracker shared] setCustomVariableWithIndex:2 name:@"App Version" value:[AppDelegate theDelegate].appVersion];
|
||||
|
||||
// The language is either the one selected by the user within the app
|
||||
// or, else, the one configured by the OS
|
||||
NSString *language = [NSBundle mxk_language] ? [NSBundle mxk_language] : [[NSBundle mainBundle] preferredLocalizations][0];
|
||||
[[PiwikTracker shared] setCustomVariableWithIndex:4 name:@"Chosen Language" value:language];
|
||||
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
if (account)
|
||||
{
|
||||
[[PiwikTracker shared] setCustomVariableWithIndex:7 name:@"Homeserver URL" value:account.mxCredentials.homeServer];
|
||||
[[PiwikTracker shared] setCustomVariableWithIndex:8 name:@"Identity Server URL" value:account.identityServerURL];
|
||||
}
|
||||
|
||||
// TODO: We should also track device and os version
|
||||
// But that needs to be decided for all platforms
|
||||
|
||||
// Catch and log crashes
|
||||
[MXLogger logCrashes:YES];
|
||||
[MXLogger setBuildVersion:[AppDelegate theDelegate].build];
|
||||
|
||||
#ifdef DEBUG
|
||||
// Disable analytics in debug as it pollutes stats
|
||||
[PiwikTracker shared].isOptedOut = YES;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[AppDelegate] The user decided to not send analytics");
|
||||
[PiwikTracker shared].isOptedOut = YES;
|
||||
[MXLogger logCrashes:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stop
|
||||
{
|
||||
[PiwikTracker shared].isOptedOut = YES;
|
||||
[MXLogger logCrashes:NO];
|
||||
}
|
||||
|
||||
- (void)trackScreen:(NSString *)screenName
|
||||
{
|
||||
// Use the same pattern as Android
|
||||
NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
|
||||
NSString *appVersion = [AppDelegate theDelegate].appVersion;
|
||||
|
||||
[[PiwikTracker shared] trackWithView:@[@"ios", appName, appVersion, screenName]
|
||||
url:nil];
|
||||
}
|
||||
|
||||
- (void)dispatch
|
||||
{
|
||||
[[PiwikTracker shared] dispatch];
|
||||
}
|
||||
|
||||
- (void)trackLaunchScreenDisplayDuration:(NSTimeInterval)seconds
|
||||
{
|
||||
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
|
||||
|
||||
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:action
|
||||
name:kMXAnalyticsStartupLaunchScreen
|
||||
number:@(seconds * 1000)
|
||||
url:nil];
|
||||
}
|
||||
|
||||
#pragma mark - MXAnalyticsDelegate
|
||||
|
||||
- (void)trackStartupStorePreloadDuration: (NSTimeInterval)seconds
|
||||
{
|
||||
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
|
||||
|
||||
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:action
|
||||
name:kMXAnalyticsStartupStorePreload
|
||||
number:@(seconds * 1000)
|
||||
url:nil];
|
||||
}
|
||||
|
||||
- (void)trackStartupMountDataDuration: (NSTimeInterval)seconds
|
||||
{
|
||||
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
|
||||
|
||||
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:action
|
||||
name:kMXAnalyticsStartupMountData
|
||||
number:@(seconds * 1000)
|
||||
url:nil];
|
||||
}
|
||||
|
||||
- (void)trackStartupSyncDuration: (NSTimeInterval)seconds isInitial: (BOOL)isInitial
|
||||
{
|
||||
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
|
||||
|
||||
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:action
|
||||
name:isInitial ? kMXAnalyticsStartupInititialSync : kMXAnalyticsStartupIncrementalSync
|
||||
number:@(seconds * 1000)
|
||||
url:nil];
|
||||
}
|
||||
|
||||
- (void)trackRoomCount: (NSUInteger)roomCount
|
||||
{
|
||||
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStatsCategory];
|
||||
|
||||
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:action
|
||||
name:kMXAnalyticsStatsRooms
|
||||
number:@(roomCount)
|
||||
url:nil];
|
||||
}
|
||||
|
||||
#pragma mark - MXDecryptionFailureDelegate
|
||||
|
||||
- (void)trackFailures:(NSDictionary<NSString *,NSNumber *> *)failuresCounts
|
||||
{
|
||||
for (NSString *reason in failuresCounts)
|
||||
{
|
||||
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsE2eCategory
|
||||
action:kAnalyticsE2eDecryptionFailureAction
|
||||
name:reason
|
||||
number:failuresCounts[reason]
|
||||
url:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
Failure reasons as defined in https://docs.google.com/document/d/1es7cTCeJEXXfRCTRgZerAM2Wg5ZerHjvlpfTW-gsOfI.
|
||||
*/
|
||||
struct DecryptionFailureReasonStruct
|
||||
{
|
||||
__unsafe_unretained NSString * const unspecified;
|
||||
__unsafe_unretained NSString * const olmKeysNotSent;
|
||||
__unsafe_unretained NSString * const olmIndexError;
|
||||
__unsafe_unretained NSString * const unexpected;
|
||||
};
|
||||
extern const struct DecryptionFailureReasonStruct DecryptionFailureReason;
|
||||
|
||||
/**
|
||||
`DecryptionFailure` represents a decryption failure.
|
||||
*/
|
||||
@interface DecryptionFailure : NSObject
|
||||
|
||||
/**
|
||||
The id of the event that was unabled to decrypt.
|
||||
*/
|
||||
@property (nonatomic) NSString *failedEventId;
|
||||
|
||||
/**
|
||||
The time the failure has been reported.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSTimeInterval ts;
|
||||
|
||||
/**
|
||||
Decryption failure reason.
|
||||
*/
|
||||
@property (nonatomic) NSString *reason;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import "DecryptionFailure.h"
|
||||
|
||||
const struct DecryptionFailureReasonStruct DecryptionFailureReason = {
|
||||
.unspecified = @"unspecified_error",
|
||||
.olmKeysNotSent = @"olm_keys_not_sent_error",
|
||||
.olmIndexError = @"olm_index_error",
|
||||
.unexpected = @"unexpected_error"
|
||||
};
|
||||
|
||||
@implementation DecryptionFailure
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
_ts = [NSDate date].timeIntervalSince1970;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "DecryptionFailure.h"
|
||||
|
||||
@import MatrixSDK;
|
||||
|
||||
@protocol MXDecryptionFailureDelegate;
|
||||
|
||||
@interface DecryptionFailureTracker : NSObject
|
||||
|
||||
/**
|
||||
Returns the shared tracker.
|
||||
|
||||
@return the shared tracker.
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
The delegate object to receive analytics events.
|
||||
*/
|
||||
@property (nonatomic) id<MXDecryptionFailureDelegate> delegate;
|
||||
|
||||
/**
|
||||
Report an event unable to decrypt.
|
||||
|
||||
This error can be momentary. The DecryptionFailureTracker will check if it gets
|
||||
fixed. Else, it will generate a failure (@see `trackFailures`).
|
||||
|
||||
@param event the event.
|
||||
@param roomState the room state when the event was received.
|
||||
@param userId my user id.
|
||||
*/
|
||||
- (void)reportUnableToDecryptErrorForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState myUser:(NSString*)userId;
|
||||
|
||||
/**
|
||||
Flush current data.
|
||||
*/
|
||||
- (void)dispatch;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
The `MXDecryptionFailureDelegate` protocol receives some stats computed by
|
||||
`DecryptionFailureTracker`.
|
||||
*/
|
||||
@protocol MXDecryptionFailureDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Stats for decryption failures.
|
||||
|
||||
@param failuresCounts the number of errors per failure reason.
|
||||
*/
|
||||
- (void)trackFailures:(NSDictionary<NSString*, NSNumber*> *)failuresCounts;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import "DecryptionFailureTracker.h"
|
||||
|
||||
|
||||
// Call `checkFailures` every `CHECK_INTERVAL`
|
||||
#define CHECK_INTERVAL 5
|
||||
|
||||
// Give events a chance to be decrypted by waiting `GRACE_PERIOD` before counting
|
||||
// and reporting them as failures
|
||||
#define GRACE_PERIOD 60
|
||||
|
||||
|
||||
@interface DecryptionFailureTracker()
|
||||
{
|
||||
// Reported failures
|
||||
// Every `CHECK_INTERVAL`, this list is checked for failures that happened
|
||||
// more than`GRACE_PERIOD` ago. Those that did are reported to the delegate.
|
||||
NSMutableDictionary<NSString* /* eventId */, DecryptionFailure*> *reportedFailures;
|
||||
|
||||
// Event ids of failures that were tracked previously
|
||||
NSMutableSet<NSString*> *trackedEvents;
|
||||
|
||||
// Timer for periodic check
|
||||
NSTimer *checkFailuresTimer;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation DecryptionFailureTracker
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
{
|
||||
static DecryptionFailureTracker *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[DecryptionFailureTracker alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
reportedFailures = [NSMutableDictionary dictionary];
|
||||
trackedEvents = [NSMutableSet set];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidDecrypt:) name:kMXEventDidDecryptNotification object:nil];
|
||||
|
||||
checkFailuresTimer = [NSTimer scheduledTimerWithTimeInterval:CHECK_INTERVAL
|
||||
target:self
|
||||
selector:@selector(checkFailures)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)reportUnableToDecryptErrorForEvent:(MXEvent *)event withRoomState:(MXRoomState *)roomState myUser:(NSString *)userId
|
||||
{
|
||||
if (reportedFailures[event.eventId] || [trackedEvents containsObject:event.eventId])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter out "expected" UTDs
|
||||
// We cannot decrypt messages sent before the user joined the room
|
||||
MXRoomMember *myUser = [roomState memberWithUserId:userId];
|
||||
if (!myUser || myUser.membership != MXMembershipJoin)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DecryptionFailure *decryptionFailure = [[DecryptionFailure alloc] init];
|
||||
decryptionFailure.failedEventId = event.eventId;
|
||||
|
||||
// Categorise the error
|
||||
switch (event.decryptionError.code)
|
||||
{
|
||||
case MXDecryptingErrorUnknownInboundSessionIdCode:
|
||||
decryptionFailure.reason = DecryptionFailureReason.olmKeysNotSent;
|
||||
break;
|
||||
|
||||
case MXDecryptingErrorOlmCode:
|
||||
decryptionFailure.reason = DecryptionFailureReason.olmIndexError;
|
||||
break;
|
||||
|
||||
case MXDecryptingErrorEncryptionNotEnabledCode:
|
||||
case MXDecryptingErrorUnableToDecryptCode:
|
||||
decryptionFailure.reason = DecryptionFailureReason.unexpected;
|
||||
break;
|
||||
|
||||
default:
|
||||
decryptionFailure.reason = DecryptionFailureReason.unspecified;
|
||||
break;
|
||||
}
|
||||
|
||||
reportedFailures[event.eventId] = decryptionFailure;
|
||||
}
|
||||
|
||||
- (void)dispatch
|
||||
{
|
||||
[self checkFailures];
|
||||
}
|
||||
|
||||
#pragma mark - Private methods
|
||||
|
||||
/**
|
||||
Mark reported failures that occured before tsNow - GRACE_PERIOD as failures that should be
|
||||
tracked.
|
||||
*/
|
||||
- (void)checkFailures
|
||||
{
|
||||
NSTimeInterval tsNow = [NSDate date].timeIntervalSince1970;
|
||||
|
||||
NSMutableArray *failuresToTrack = [NSMutableArray array];
|
||||
|
||||
for (DecryptionFailure *reportedFailure in reportedFailures.allValues)
|
||||
{
|
||||
if (reportedFailure.ts < tsNow - GRACE_PERIOD)
|
||||
{
|
||||
[failuresToTrack addObject:reportedFailure];
|
||||
[reportedFailures removeObjectForKey:reportedFailure.failedEventId];
|
||||
[trackedEvents addObject:reportedFailure.failedEventId];
|
||||
}
|
||||
}
|
||||
|
||||
if (_delegate && failuresToTrack.count)
|
||||
{
|
||||
// Sort failures by error reason
|
||||
NSMutableDictionary<NSString*, NSNumber*> *failuresCounts = [NSMutableDictionary dictionary];
|
||||
for (DecryptionFailure *failure in failuresToTrack)
|
||||
{
|
||||
failuresCounts[failure.reason] = @(failuresCounts[failure.reason].unsignedIntegerValue + 1);
|
||||
}
|
||||
|
||||
NSLog(@"[DecryptionFailureTracker] trackFailures: %@", failuresCounts);
|
||||
|
||||
[_delegate trackFailures:failuresCounts];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)eventDidDecrypt:(NSNotification *)notif
|
||||
{
|
||||
// Could be an event in the reportedFailures, remove it
|
||||
MXEvent *event = notif.object;
|
||||
[reportedFailures removeObjectForKey:event.eventId];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user