Merge branch 'charlesmchen/analytics6'

pull/1/head
Matthew Chen 7 years ago
commit 92f053e7ae

@ -35,7 +35,6 @@
3471B1DA1EB7C63600F6AEC8 /* NewNonContactConversationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3471B1D91EB7C63600F6AEC8 /* NewNonContactConversationViewController.m */; };
3472229F1EB22FFE00E53955 /* AddToGroupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3472229E1EB22FFE00E53955 /* AddToGroupViewController.m */; };
348F2EAE1F0D21BC00D4ECE0 /* DeviceSleepManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */; };
3495BC901F1400FF00B478F5 /* OWSAsserts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3495BC8F1F1400FF00B478F5 /* OWSAsserts.swift */; };
3497DBEC1ECE257500DB2605 /* OWSCountryMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 3497DBEB1ECE257500DB2605 /* OWSCountryMetadata.m */; };
3497DBEF1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3497DBEE1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m */; };
34B3F8711E8DF1700035BE1A /* AboutTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8351E8DF1700035BE1A /* AboutTableViewController.m */; };
@ -82,6 +81,7 @@
34D8C0271ED3673300188D7C /* DebugUIMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C0241ED3673300188D7C /* DebugUIMessages.m */; };
34D8C0281ED3673300188D7C /* DebugUITableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C0261ED3673300188D7C /* DebugUITableViewController.m */; };
34D8C02B1ED3685800188D7C /* DebugUIContacts.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C02A1ED3685800188D7C /* DebugUIContacts.m */; };
34D99C8E1F28DDCD00D284D6 /* OWSSwiftUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D99C8D1F28DDCD00D284D6 /* OWSSwiftUtils.swift */; };
34DFCB851E8E04B500053165 /* AddToBlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */; };
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */; };
34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E3EF0C1EFC235B007F6822 /* DebugUIDiskUsage.m */; };
@ -113,7 +113,6 @@
451DA3C91F1489BE008E2423 /* OWSAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3453D8E91EC0D4ED003F9E6F /* OWSAlerts.swift */; };
451DA3CA1F148A9F008E2423 /* DeviceSleepManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */; };
451DA3CB1F148AAD008E2423 /* CallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F83B1E8DF1700035BE1A /* CallViewController.swift */; };
451DA3CF1F1655CD008E2423 /* OWSAsserts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3495BC8F1F1400FF00B478F5 /* OWSAsserts.swift */; };
451DE9F81DC18C9500810E42 /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD81EE1DC030E7004C9430 /* AccountManager.swift */; };
451DE9FD1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */; };
451DE9FE1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */; };
@ -424,7 +423,6 @@
3472229D1EB22FFE00E53955 /* AddToGroupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToGroupViewController.h; sourceTree = "<group>"; };
3472229E1EB22FFE00E53955 /* AddToGroupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToGroupViewController.m; sourceTree = "<group>"; };
348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceSleepManager.swift; sourceTree = "<group>"; };
3495BC8F1F1400FF00B478F5 /* OWSAsserts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSAsserts.swift; sourceTree = "<group>"; };
3495BC911F1426B800B478F5 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = translations/ar.lproj/Localizable.strings; sourceTree = "<group>"; };
3497DBEA1ECE257500DB2605 /* OWSCountryMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSCountryMetadata.h; sourceTree = "<group>"; };
3497DBEB1ECE257500DB2605 /* OWSCountryMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSCountryMetadata.m; sourceTree = "<group>"; };
@ -511,6 +509,7 @@
34D8C0261ED3673300188D7C /* DebugUITableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = DebugUITableViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
34D8C0291ED3685800188D7C /* DebugUIContacts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIContacts.h; sourceTree = "<group>"; };
34D8C02A1ED3685800188D7C /* DebugUIContacts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIContacts.m; sourceTree = "<group>"; };
34D99C8D1F28DDCD00D284D6 /* OWSSwiftUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSSwiftUtils.swift; sourceTree = "<group>"; };
34DFCB831E8E04B400053165 /* AddToBlockListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToBlockListViewController.h; sourceTree = "<group>"; };
34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToBlockListViewController.m; sourceTree = "<group>"; };
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioProgressView.swift; sourceTree = "<group>"; };
@ -1351,7 +1350,6 @@
BFB074C619A5611000F2947C /* ObservableValue.m */,
76EB04EE18170B33006006FC /* Operation.h */,
76EB04EF18170B33006006FC /* Operation.m */,
3495BC8F1F1400FF00B478F5 /* OWSAsserts.swift */,
34D5CC941EA6AFAD005515DB /* OWSContactsSyncing.h */,
34D5CC951EA6AFAD005515DB /* OWSContactsSyncing.m */,
45CD81F01DC03A22004C9430 /* OWSLogger.h */,
@ -1359,6 +1357,7 @@
34009B651EC4CB11001D95D1 /* OWSMath.h */,
45666F541D9B2827008FE134 /* OWSScrubbingLogFormatter.h */,
45666F551D9B2827008FE134 /* OWSScrubbingLogFormatter.m */,
34D99C8D1F28DDCD00D284D6 /* OWSSwiftUtils.swift */,
4579431C1E7C8CE9008ED0C0 /* Pastelog.h */,
4579431D1E7C8CE9008ED0C0 /* Pastelog.m */,
450DF2041E0D74AC003D14BE /* Platform.swift */,
@ -2168,7 +2167,6 @@
45666EC91D994C0D008FE134 /* OWSGroupAvatarBuilder.m in Sources */,
3471B1DA1EB7C63600F6AEC8 /* NewNonContactConversationViewController.m in Sources */,
34B3F87C1E8DF1700035BE1A /* FingerprintViewController.m in Sources */,
3495BC901F1400FF00B478F5 /* OWSAsserts.swift in Sources */,
76EB058218170B33006006FC /* Environment.m in Sources */,
34B3F8921E8DF1710035BE1A /* SignalAttachment.swift in Sources */,
45464DBC1DFA041F001D3FD6 /* DataChannelMessage.swift in Sources */,
@ -2232,6 +2230,7 @@
3448BFD01EDF0EA7005B2D69 /* MessagesViewController.m in Sources */,
34CCAF3B1F0C2748004084F4 /* OWSAddToContactViewController.m in Sources */,
45F659731E1BD99C00444429 /* CallKitCallUIAdaptee.swift in Sources */,
34D99C8E1F28DDCD00D284D6 /* OWSSwiftUtils.swift in Sources */,
45BB93381E688E14001E3939 /* UIDevice+featureSupport.swift in Sources */,
458DE9D61DEE3FD00071BB03 /* PeerConnectionClient.swift in Sources */,
451DE9FD1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */,
@ -2292,7 +2291,6 @@
452C46901E427E200087B011 /* OutboundCallInitiator.swift in Sources */,
451DE9F81DC18C9500810E42 /* AccountManager.swift in Sources */,
B660F7171C29988E00687D6E /* OWSContactsManager.m in Sources */,
451DA3CF1F1655CD008E2423 /* OWSAsserts.swift in Sources */,
B660F7181C29988E00687D6E /* CryptoTools.m in Sources */,
B660F71B1C29988E00687D6E /* Environment.m in Sources */,
B660F71C1C29988E00687D6E /* DebugLogger.m in Sources */,

@ -46,6 +46,7 @@
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
#import <SignalServiceKit/NSTimer+OWS.h>
#import <SignalServiceKit/OWSAcknowledgeMessageDeliveryRequest.h>
#import <SignalServiceKit/OWSAnalytics.h>
#import <SignalServiceKit/OWSAttachmentsProcessor.h>
#import <SignalServiceKit/OWSCallAnswerMessage.h>
#import <SignalServiceKit/OWSCallBusyMessage.h>

@ -81,6 +81,11 @@ enum CallError: Error {
// Should be roughly synced with Android client for consistency
fileprivate let connectingTimeoutSeconds = 120
func OWSProdCallAssertionError(description: String, file: String, function: String, line: Int32) -> CallError {
OWSProdError(description, file:file, function:function, line:line)
return .assertionError(description: description)
}
// All Observer methods will be invoked from the main thread.
protocol CallServiceObserver: class {
/**
@ -273,7 +278,7 @@ protocol CallServiceObserver: class {
let errorDescription = "\(TAG) call was unexpectedly already set."
Logger.error(errorDescription)
call.state = .localFailure
return Promise(error: CallError.assertionError(description: errorDescription))
return Promise(error: OWSProdCallAssertionError(description: errorDescription, file:#file, function:#function, line:#line))
}
self.call = call
@ -295,7 +300,7 @@ protocol CallServiceObserver: class {
guard self.peerConnectionClient == nil else {
let errorDescription = "\(self.TAG) peerconnection was unexpectedly already set."
Logger.error(errorDescription)
throw CallError.assertionError(description: errorDescription)
throw OWSProdCallAssertionError(description: errorDescription, file:#file, function:#function, line:#line)
}
let useTurnOnly = Environment.getCurrent().preferences.doCallsHideIPAddress()
@ -371,7 +376,7 @@ protocol CallServiceObserver: class {
}
guard let fulfillReadyToSendIceUpdatesPromise = self.fulfillReadyToSendIceUpdatesPromise else {
self.handleFailedCall(failedCall: call, error: .assertionError(description: "failed to create fulfillReadyToSendIceUpdatesPromise"))
self.handleFailedCall(failedCall: call, error: OWSProdCallAssertionError(description: "failed to create fulfillReadyToSendIceUpdatesPromise", file:#file, function:#function, line:#line))
return
}
@ -409,7 +414,7 @@ protocol CallServiceObserver: class {
}
guard let peerConnectionClient = self.peerConnectionClient else {
handleFailedCall(failedCall: call, error: CallError.assertionError(description: "peerConnectionClient was unexpectedly nil in \(#function)"))
handleFailedCall(failedCall: call, error: OWSProdCallAssertionError(description: "peerConnectionClient was unexpectedly nil in \(#function)", file:#file, function:#function, line:#line))
return
}
@ -697,7 +702,7 @@ protocol CallServiceObserver: class {
AssertIsOnMainThread()
guard let call = self.call else {
self.handleFailedCurrentCall(error: .assertionError(description: "ignoring local ice candidate, since there is no current call."))
self.handleFailedCurrentCall(error: OWSProdCallAssertionError(description: "ignoring local ice candidate, since there is no current call.", file:#file, function:#function, line:#line))
return
}
@ -712,7 +717,7 @@ protocol CallServiceObserver: class {
guard call.state != .idle else {
// This will only be called for the current peerConnectionClient, so
// fail the current call.
self.handleFailedCurrentCall(error: .assertionError(description: "ignoring local ice candidate, since call is now idle."))
self.handleFailedCurrentCall(error: OWSProdCallAssertionError(description: "ignoring local ice candidate, since call is now idle.", file:#file, function:#function, line:#line))
return
}
@ -748,7 +753,7 @@ protocol CallServiceObserver: class {
guard let call = self.call else {
// This will only be called for the current peerConnectionClient, so
// fail the current call.
handleFailedCurrentCall(error: .assertionError(description:"\(TAG) ignoring \(#function) since there is no current call."))
handleFailedCurrentCall(error: OWSProdCallAssertionError(description:"\(TAG) ignoring \(#function) since there is no current call.", file:#file, function:#function, line:#line))
return
}
@ -817,14 +822,14 @@ protocol CallServiceObserver: class {
guard let call = self.call else {
// This should never happen; return to a known good state.
owsFail("\(TAG) call was unexpectedly nil in \(#function)")
handleFailedCurrentCall(error: .assertionError(description:"\(TAG) call was unexpectedly nil in \(#function)"))
handleFailedCurrentCall(error: OWSProdCallAssertionError(description:"\(TAG) call was unexpectedly nil in \(#function)", file:#file, function:#function, line:#line))
return
}
guard call.localId == localId else {
// This should never happen; return to a known good state.
owsFail("\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)")
handleFailedCurrentCall(error: .assertionError(description:"\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)"))
handleFailedCurrentCall(error: OWSProdCallAssertionError(description:"\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)", file:#file, function:#function, line:#line))
return
}
@ -840,7 +845,7 @@ protocol CallServiceObserver: class {
Logger.debug("\(TAG) in \(#function)")
guard let currentCall = self.call else {
handleFailedCall(failedCall: call, error: .assertionError(description:"\(TAG) ignoring \(#function) since there is no current call"))
handleFailedCall(failedCall: call, error: OWSProdCallAssertionError(description:"\(TAG) ignoring \(#function) since there is no current call", file:#file, function:#function, line:#line))
return
}
@ -852,7 +857,7 @@ protocol CallServiceObserver: class {
}
guard let peerConnectionClient = self.peerConnectionClient else {
handleFailedCall(failedCall: call, error: .assertionError(description:"\(TAG) missing peerconnection client in \(#function)"))
handleFailedCall(failedCall: call, error: OWSProdCallAssertionError(description:"\(TAG) missing peerconnection client in \(#function)", file:#file, function:#function, line:#line))
return
}
@ -877,7 +882,7 @@ protocol CallServiceObserver: class {
AssertIsOnMainThread()
guard let peerConnectionClient = self.peerConnectionClient else {
handleFailedCall(failedCall: call, error: .assertionError(description:"\(TAG) peerConnectionClient unexpectedly nil in \(#function)"))
handleFailedCall(failedCall: call, error: OWSProdCallAssertionError(description:"\(TAG) peerConnectionClient unexpectedly nil in \(#function)", file:#file, function:#function, line:#line))
return
}
@ -907,14 +912,14 @@ protocol CallServiceObserver: class {
guard let call = self.call else {
// This should never happen; return to a known good state.
owsFail("\(TAG) call was unexpectedly nil in \(#function)")
handleFailedCurrentCall(error: .assertionError(description:"\(TAG) call was unexpectedly nil in \(#function)"))
handleFailedCurrentCall(error: OWSProdCallAssertionError(description:"\(TAG) call was unexpectedly nil in \(#function)", file:#file, function:#function, line:#line))
return
}
guard call.localId == localId else {
// This should never happen; return to a known good state.
owsFail("\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)")
handleFailedCurrentCall(error: .assertionError(description:"\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)"))
handleFailedCurrentCall(error: OWSProdCallAssertionError(description:"\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)", file:#file, function:#function, line:#line))
return
}
@ -953,17 +958,17 @@ protocol CallServiceObserver: class {
AssertIsOnMainThread()
guard let currentCall = self.call else {
handleFailedCall(failedCall: call, error: .assertionError(description:"\(TAG) ignoring \(#function) since there is no current call"))
handleFailedCall(failedCall: call, error: OWSProdCallAssertionError(description:"\(TAG) ignoring \(#function) since there is no current call", file:#file, function:#function, line:#line))
return
}
guard call == currentCall else {
handleFailedCall(failedCall: call, error: .assertionError(description:"\(TAG) ignoring \(#function) for call other than current call"))
handleFailedCall(failedCall: call, error: OWSProdCallAssertionError(description:"\(TAG) ignoring \(#function) for call other than current call", file:#file, function:#function, line:#line))
return
}
guard let peerConnectionClient = self.peerConnectionClient else {
handleFailedCall(failedCall: call, error: .assertionError(description:"\(TAG) missing peerconnection client in \(#function)"))
handleFailedCall(failedCall: call, error: OWSProdCallAssertionError(description:"\(TAG) missing peerconnection client in \(#function)", file:#file, function:#function, line:#line))
return
}
@ -1003,7 +1008,7 @@ protocol CallServiceObserver: class {
guard let call = self.call else {
// This should never happen; return to a known good state.
owsFail("\(TAG) call was unexpectedly nil in \(#function)")
handleFailedCurrentCall(error: .assertionError(description:"\(TAG) call unexpectedly nil in \(#function)"))
handleFailedCurrentCall(error: OWSProdCallAssertionError(description:"\(TAG) call unexpectedly nil in \(#function)", file:#file, function:#function, line:#line))
return
}
@ -1057,7 +1062,7 @@ protocol CallServiceObserver: class {
guard let call = self.call else {
// This should never happen; return to a known good state.
owsFail("\(TAG) call was unexpectedly nil in \(#function)")
handleFailedCurrentCall(error: .assertionError(description:"\(TAG) call unexpectedly nil in \(#function)"))
handleFailedCurrentCall(error: OWSProdCallAssertionError(description:"\(TAG) call unexpectedly nil in \(#function)", file:#file, function:#function, line:#line))
return
}
@ -1095,7 +1100,7 @@ protocol CallServiceObserver: class {
guard let call = self.call else {
// This should never happen; return to a known good state.
owsFail("\(TAG) received data message, but there is no current call. Ignoring.")
handleFailedCurrentCall(error: .assertionError(description:"\(TAG) received data message, but there is no current call. Ignoring."))
handleFailedCurrentCall(error: OWSProdCallAssertionError(description:"\(TAG) received data message, but there is no current call. Ignoring.", file:#file, function:#function, line:#line))
return
}
@ -1107,7 +1112,7 @@ protocol CallServiceObserver: class {
guard connected.id == call.signalingId else {
// This should never happen; return to a known good state.
owsFail("\(TAG) received connected message for call with id:\(connected.id) but current call has id:\(call.signalingId)")
handleFailedCurrentCall(error: .assertionError(description:"\(TAG) received connected message for call with id:\(connected.id) but current call has id:\(call.signalingId)"))
handleFailedCurrentCall(error: OWSProdCallAssertionError(description:"\(TAG) received connected message for call with id:\(connected.id) but current call has id:\(call.signalingId)", file:#file, function:#function, line:#line))
return
}
@ -1122,7 +1127,7 @@ protocol CallServiceObserver: class {
guard hangup.id == call.signalingId else {
// This should never happen; return to a known good state.
owsFail("\(TAG) received hangup message for call with id:\(hangup.id) but current call has id:\(call.signalingId)")
handleFailedCurrentCall(error: .assertionError(description:"\(TAG) received hangup message for call with id:\(hangup.id) but current call has id:\(call.signalingId)"))
handleFailedCurrentCall(error: OWSProdCallAssertionError(description:"\(TAG) received hangup message for call with id:\(hangup.id) but current call has id:\(call.signalingId)", file:#file, function:#function, line:#line))
return
}
@ -1229,7 +1234,7 @@ protocol CallServiceObserver: class {
}
guard let readyToSendIceUpdatesPromise = self.readyToSendIceUpdatesPromise else {
return Promise(error: CallError.assertionError(description: "failed to create readyToSendIceUpdatesPromise"))
return Promise(error: OWSProdCallAssertionError(description: "failed to create readyToSendIceUpdatesPromise", file:#file, function:#function, line:#line))
}
return readyToSendIceUpdatesPromise
@ -1272,7 +1277,7 @@ protocol CallServiceObserver: class {
}
guard let peerConnectionClientPromise = self.peerConnectionClientPromise else {
return Promise(error: CallError.assertionError(description: "failed to create peerConnectionClientPromise"))
return Promise(error: OWSProdCallAssertionError(description: "failed to create peerConnectionClientPromise", file:#file, function:#function, line:#line))
}
return peerConnectionClientPromise

@ -720,11 +720,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
* We synchronize access to state in this class using this queue.
*/
private func assertOnSignalingQueue() {
if #available(iOS 10.0, *) {
dispatchPrecondition(condition: .onQueue(type(of: self).signalingQueue))
} else {
// Skipping check on <iOS10, since syntax is different and it's just a development convenience.
}
assertOnQueue(type(of: self).signalingQueue)
}
// MARK: Test-only accessors

@ -1,9 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
func owsFail(_ message: String) {
Logger.error(message)
Logger.flush()
assertionFailure(message)
}

@ -0,0 +1,33 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
import Foundation
//import PromiseKit
//import WebRTC
// MARK: Helpers
/**
* We synchronize access to state in this class using this queue.
*/
func assertOnQueue(_ queue: DispatchQueue) {
if #available(iOS 10.0, *) {
dispatchPrecondition(condition: .onQueue(queue))
} else {
// Skipping check on <iOS10, since syntax is different and it's just a development convenience.
}
}
func owsFail(_ message: String) {
Logger.error(message)
Logger.flush()
assertionFailure(message)
}
// Example: OWSProdError("blah", #file, #function, #line)
func OWSProdError(_ eventName: String, file: String, function: String, line: Int32) {
let location = "\((file as NSString).lastPathComponent):\(function)"
OWSAnalytics
.logEvent(eventName, severity: .error, parameters: nil, location: (location as NSString).utf8String!, line:line)
}

@ -120,8 +120,6 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
//
// The best we can try to do is to discard the current database
// and behave like a clean install.
// NOTE: This analytics event will never be delivered, since the database isn't working.
OWSProdCritical(@"storage_error_could_not_load_database");
// Try to reset app by deleting database.
@ -129,9 +127,11 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
// [self resetSignalStorage];
if (![self tryToLoadDatabase]) {
// NOTE: This analytics event will never be delivered, since the database isn't working.
OWSProdCritical(@"storage_error_could_not_load_database_second_attempt");
// Sleep to give analytics events time to be delivered.
[NSThread sleepForTimeInterval:15.0f];
[NSException raise:TSStorageManagerExceptionNameNoDatabase format:@"Failed to initialize database."];
}
@ -262,9 +262,7 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
BOOL success = [ressourceURL setResourceValues:resourcesAttrs error:&error];
if (error || !success) {
// NOTE: This analytics event will never be delivered, since the database isn't working.
OWSProdCriticalWNSError(@"storage_error_file_protection", error);
return;
}
}
@ -327,6 +325,9 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
{
OWSAssert([UIApplication sharedApplication].applicationState == UIApplicationStateBackground);
// Sleep to give analytics events time to be delivered.
[NSThread sleepForTimeInterval:5.0f];
// Presumably this happened in response to a push notification. It's possible that the keychain is corrupted
// but it could also just be that the user hasn't yet unlocked their device since our password is
// kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
@ -361,7 +362,6 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
BOOL shouldHavePassword = [NSFileManager.defaultManager fileExistsAtPath:[self dbPath]];
if (shouldHavePassword) {
// NOTE: This analytics event will never be delivered, since the database isn't working.
OWSProdCritical(@"storage_error_could_not_load_database_second_attempt");
}
@ -381,11 +381,13 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
NSError *keySetError;
[SAMKeychain setPassword:newDBPassword forService:keychainService account:keychainDBPassAccount error:&keySetError];
if (keySetError) {
// NOTE: This analytics event will never be delivered, since the database isn't working.
OWSProdCriticalWNSError(@"storage_error_could_not_store_database_password", keySetError);
[self deletePasswordFromKeychain];
// Sleep to give analytics events time to be delivered.
[NSThread sleepForTimeInterval:15.0f];
[NSException raise:TSStorageManagerExceptionNameDatabasePasswordUnwritable
format:@"Setting DB password failed with error: %@", keySetError];
} else {

@ -12,10 +12,8 @@ typedef NS_ENUM(NSUInteger, OWSAnalyticsSeverity) {
OWSAnalyticsSeverityInfo = 1,
// Error events should never be discarded.
OWSAnalyticsSeverityError = 3,
// Critical events should never be discarded.
//
// Additionally, to avoid losing critical events they should
// be persisted synchronously.
// Critical events are special. They are submitted immediately
// and not persisted, since the database may not be working.
OWSAnalyticsSeverityCritical = 4
};
@ -83,12 +81,12 @@ typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)()
// parametersBlock is of type OWSProdAssertParametersBlock.
// The "C" variants (e.g. OWSProdAssert() vs. OWSProdCAssert() should be used in free functions,
// where there is no self. They can also be used in blocks to avoid capturing a reference to self.
#define OWSProdAssertWParamsTemplate(__value, __analyticsEventName, __parametersBlock, __assertMacro) \
#define OWSProdAssertWParamsTemplate(__value, __eventName, __parametersBlock, __assertMacro) \
{ \
if (!(BOOL)(__value)) { \
NSDictionary<NSString *, id> *__eventParameters = (__parametersBlock ? __parametersBlock() : nil); \
[DDLog flushLog]; \
[OWSAnalytics logEvent:__analyticsEventName \
[OWSAnalytics logEvent:__eventName \
severity:OWSAnalyticsSeverityError \
parameters:__eventParameters \
location:__PRETTY_FUNCTION__ \
@ -98,64 +96,64 @@ typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)()
return (BOOL)(__value); \
}
#define OWSProdAssertWParams(__value, __analyticsEventName, __parametersBlock) \
OWSProdAssertWParamsTemplate(__value, __analyticsEventName, __parametersBlock, OWSAssert)
#define OWSProdAssertWParams(__value, __eventName, __parametersBlock) \
OWSProdAssertWParamsTemplate(__value, __eventName, __parametersBlock, OWSAssert)
#define OWSProdCAssertWParams(__value, __analyticsEventName, __parametersBlock) \
OWSProdAssertWParamsTemplate(__value, __analyticsEventName, __parametersBlock, OWSCAssert)
#define OWSProdCAssertWParams(__value, __eventName, __parametersBlock) \
OWSProdAssertWParamsTemplate(__value, __eventName, __parametersBlock, OWSCAssert)
#define OWSProdAssert(__value, __analyticsEventName) OWSProdAssertWParams(__value, __analyticsEventName, nil)
#define OWSProdAssert(__value, __eventName) OWSProdAssertWParams(__value, __eventName, nil)
#define OWSProdCAssert(__value, __analyticsEventName) OWSProdCAssertWParams(__value, __analyticsEventName, nil)
#define OWSProdCAssert(__value, __eventName) OWSProdCAssertWParams(__value, __eventName, nil)
#define OWSProdFailWParamsTemplate(__analyticsEventName, __parametersBlock, __failMacro) \
#define OWSProdFailWParamsTemplate(__eventName, __parametersBlock, __failMacro) \
{ \
NSDictionary<NSString *, id> *__eventParameters \
= (__parametersBlock ? ((OWSProdAssertParametersBlock)__parametersBlock)() : nil); \
[OWSAnalytics logEvent:__analyticsEventName \
[OWSAnalytics logEvent:__eventName \
severity:OWSAnalyticsSeverityCritical \
parameters:__eventParameters \
location:__PRETTY_FUNCTION__ \
line:__LINE__]; \
__failMacro(__analyticsEventName); \
__failMacro(__eventName); \
}
#define OWSProdFailWParams(__analyticsEventName, __parametersBlock) \
OWSProdFailWParamsTemplate(__analyticsEventName, __parametersBlock, OWSFail)
#define OWSProdCFailWParams(__analyticsEventName, __parametersBlock) \
OWSProdFailWParamsTemplate(__analyticsEventName, __parametersBlock, OWSCFail)
#define OWSProdFailWParams(__eventName, __parametersBlock) \
OWSProdFailWParamsTemplate(__eventName, __parametersBlock, OWSFail)
#define OWSProdCFailWParams(__eventName, __parametersBlock) \
OWSProdFailWParamsTemplate(__eventName, __parametersBlock, OWSCFail)
#define OWSProdFail(__analyticsEventName) OWSProdFailWParams(__analyticsEventName, nil)
#define OWSProdFail(__eventName) OWSProdFailWParams(__eventName, nil)
#define OWSProdCFail(__analyticsEventName) OWSProdCFailWParams(__analyticsEventName, nil)
#define OWSProdCFail(__eventName) OWSProdCFailWParams(__eventName, nil)
// The debug logs can be more verbose than the analytics events.
//
// In this case `debugDescription` is valuable enough to
// log but too dangerous to include in the analytics event.
#define OWSProdFailWNSError(__analyticsEventName, __nserror) \
#define OWSProdFailWNSError(__eventName, __nserror) \
{ \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __nserror.debugDescription); \
OWSProdFailWParams(__analyticsEventName, AnalyticsParametersFromNSError(__nserror)) \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __eventName, __nserror.debugDescription); \
OWSProdFailWParams(__eventName, AnalyticsParametersFromNSError(__nserror)) \
}
// The debug logs can be more verbose than the analytics events.
//
// In this case `exception` is valuable enough to
// log but too dangerous to include in the analytics event.
#define OWSProdFailWNSException(__analyticsEventName, __exception) \
#define OWSProdFailWNSException(__eventName, __exception) \
{ \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __exception); \
OWSProdFailWParams(__analyticsEventName, AnalyticsParametersFromNSException(__exception)) \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __eventName, __exception); \
OWSProdFailWParams(__eventName, AnalyticsParametersFromNSException(__exception)) \
}
#define OWSProdCFail(__analyticsEventName) OWSProdCFailWParams(__analyticsEventName, nil)
#define OWSProdCFail(__eventName) OWSProdCFailWParams(__eventName, nil)
#define OWSProdEventWParams(__severityLevel, __analyticsEventName, __parametersBlock) \
#define OWSProdEventWParams(__severityLevel, __eventName, __parametersBlock) \
{ \
NSDictionary<NSString *, id> *__eventParameters \
= (__parametersBlock ? ((OWSProdAssertParametersBlock)__parametersBlock)() : nil); \
[OWSAnalytics logEvent:__analyticsEventName \
[OWSAnalytics logEvent:__eventName \
severity:__severityLevel \
parameters:__eventParameters \
location:__PRETTY_FUNCTION__ \
@ -164,60 +162,59 @@ typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)()
#pragma mark - Info Events
#define OWSProdInfoWParams(__analyticsEventName, __parametersBlock) \
OWSProdEventWParams(OWSAnalyticsSeverityInfo, __analyticsEventName, __parametersBlock)
#define OWSProdInfoWParams(__eventName, __parametersBlock) \
OWSProdEventWParams(OWSAnalyticsSeverityInfo, __eventName, __parametersBlock)
#define OWSProdInfo(__analyticsEventName) OWSProdEventWParams(OWSAnalyticsSeverityInfo, __analyticsEventName, nil)
#define OWSProdInfo(__eventName) OWSProdEventWParams(OWSAnalyticsSeverityInfo, __eventName, nil)
#pragma mark - Error Events
#define OWSProdErrorWParams(__analyticsEventName, __parametersBlock) \
OWSProdEventWParams(OWSAnalyticsSeverityError, __analyticsEventName, __parametersBlock)
#define OWSProdErrorWParams(__eventName, __parametersBlock) \
OWSProdEventWParams(OWSAnalyticsSeverityError, __eventName, __parametersBlock)
#define OWSProdError(__analyticsEventName) OWSProdEventWParams(OWSAnalyticsSeverityError, __analyticsEventName, nil)
#define OWSProdError(__eventName) OWSProdEventWParams(OWSAnalyticsSeverityError, __eventName, nil)
// The debug logs can be more verbose than the analytics events.
//
// In this case `debugDescription` is valuable enough to
// log but too dangerous to include in the analytics event.
#define OWSProdErrorWNSError(__analyticsEventName, __nserror) \
#define OWSProdErrorWNSError(__eventName, __nserror) \
{ \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __nserror.debugDescription); \
OWSProdErrorWParams(__analyticsEventName, AnalyticsParametersFromNSError(__nserror)) \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __eventName, __nserror.debugDescription); \
OWSProdErrorWParams(__eventName, AnalyticsParametersFromNSError(__nserror)) \
}
// The debug logs can be more verbose than the analytics events.
//
// In this case `exception` is valuable enough to
// log but too dangerous to include in the analytics event.
#define OWSProdErrorWNSException(__analyticsEventName, __exception) \
#define OWSProdErrorWNSException(__eventName, __exception) \
{ \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __exception); \
OWSProdErrorWParams(__analyticsEventName, AnalyticsParametersFromNSException(__exception)) \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __eventName, __exception); \
OWSProdErrorWParams(__eventName, AnalyticsParametersFromNSException(__exception)) \
}
#pragma mark - Critical Events
#define OWSProdCriticalWParams(__analyticsEventName, __parametersBlock) \
OWSProdEventWParams(OWSAnalyticsSeverityCritical, __analyticsEventName, __parametersBlock)
#define OWSProdCriticalWParams(__eventName, __parametersBlock) \
OWSProdEventWParams(OWSAnalyticsSeverityCritical, __eventName, __parametersBlock)
#define OWSProdCritical(__analyticsEventName) \
OWSProdEventWParams(OWSAnalyticsSeverityCritical, __analyticsEventName, nil)
#define OWSProdCritical(__eventName) OWSProdEventWParams(OWSAnalyticsSeverityCritical, __eventName, nil)
#define OWSProdCriticalWNSError(__analyticsEventName, __nserror) \
#define OWSProdCriticalWNSError(__eventName, __nserror) \
{ \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __nserror.debugDescription); \
OWSProdCriticalWParams(__analyticsEventName, AnalyticsParametersFromNSError(__nserror)) \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __eventName, __nserror.debugDescription); \
OWSProdCriticalWParams(__eventName, AnalyticsParametersFromNSError(__nserror)) \
}
// The debug logs can be more verbose than the analytics events.
//
// In this case `exception` is valuable enough to
// log but too dangerous to include in the analytics event.
#define OWSProdCriticalWNSException(__analyticsEventName, __exception) \
#define OWSProdCriticalWNSException(__eventName, __exception) \
{ \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __exception); \
OWSProdCriticalWParams(__analyticsEventName, AnalyticsParametersFromNSException(__exception)) \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __eventName, __exception); \
OWSProdCriticalWParams(__eventName, AnalyticsParametersFromNSException(__exception)) \
}
NS_ASSUME_NONNULL_END

@ -4,6 +4,7 @@
#import "OWSAnalytics.h"
#import "AppVersion.h"
#import "OWSQueues.h"
#import "TSStorageManager.h"
#import <CocoaLumberjack/CocoaLumberjack.h>
#import <Reachability/Reachability.h>
@ -34,9 +35,20 @@ NSString *const kOWSAnalytics_KeyLaunchCompleteCount = @"kOWSAnalytics_KeyLaunch
// Percentage of analytics events to discard. 0 <= x <= 100.
const int kOWSAnalytics_DiscardFrequency = 0;
NSString *NSStringForOWSAnalyticsSeverity(OWSAnalyticsSeverity severity)
{
switch (severity) {
case OWSAnalyticsSeverityInfo:
return @"Info";
case OWSAnalyticsSeverityError:
return @"Error";
case OWSAnalyticsSeverityCritical:
return @"Critical";
}
}
@interface OWSAnalytics ()
@property (nonatomic, readonly) TSStorageManager *storageManager;
@property (nonatomic, readonly) Reachability *reachability;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@ -56,20 +68,27 @@ const int kOWSAnalytics_DiscardFrequency = 0;
static OWSAnalytics *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [self new];
instance = [[self alloc] initDefault];
});
return instance;
}
- (instancetype)initDefault
// We lazy-create the analytics DB connection, so that we can handle
// errors that occur while initializing TSStorageManager.
+ (YapDatabaseConnection *)dbConnection
{
TSStorageManager *storageManager = [TSStorageManager sharedManager];
return [self initWithStorageManager:storageManager];
static YapDatabaseConnection *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
TSStorageManager *storageManager = [TSStorageManager sharedManager];
OWSAssert(storageManager);
// Use a newDatabaseConnection so as not to block reads in the launch path.
instance = storageManager.newDatabaseConnection;
});
return instance;
}
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
- (instancetype)initDefault
{
self = [super init];
@ -77,11 +96,6 @@ const int kOWSAnalytics_DiscardFrequency = 0;
return self;
}
OWSAssert(storageManager);
_storageManager = storageManager;
// Use a newDatabaseConnection so as not to block other reads in the launch path.
_dbConnection = storageManager.newDatabaseConnection;
_reachability = [Reachability reachabilityForInternetConnection];
[self observeNotifications];
@ -129,7 +143,11 @@ const int kOWSAnalytics_DiscardFrequency = 0;
//
// * There's no network available.
// * There's already a sync request in flight.
if (!self.reachability.isReachable || self.hasRequestInFlight) {
if (!self.reachability.isReachable) {
DDLogVerbose(@"%@ Not reachable", self.tag);
return;
}
if (self.hasRequestInFlight) {
return;
}
@ -145,46 +163,106 @@ const int kOWSAnalytics_DiscardFrequency = 0;
if (!firstEventKey) {
return;
}
firstEventDictionary = [transaction objectForKey:firstEventKey inCollection:kOWSAnalytics_EventsCollection];
OWSAssert(firstEventDictionary);
OWSAssert([firstEventDictionary isKindOfClass:[NSDictionary class]]);
}];
if (!firstEventDictionary) {
return;
if (firstEventDictionary) {
[self sendEvent:firstEventDictionary eventKey:firstEventKey isCritical:NO];
}
});
}
DDLogDebug(@"%@ trying to deliver event: %@", self.tag, firstEventKey);
- (void)sendEvent:(NSDictionary *)eventDictionary eventKey:(NSString *)eventKey isCritical:(BOOL)isCritical
{
OWSAssert(eventDictionary);
OWSAssert(eventKey);
AssertOnDispatchQueue(self.serialQueue);
if (isCritical) {
[self submitEvent:eventDictionary
eventKey:eventKey
success:^{
DDLogDebug(@"%@ sendEvent[critical] succeeded: %@", self.tag, eventKey);
}
failure:^{
DDLogError(@"%@ sendEvent[critical] failed: %@", self.tag, eventKey);
}];
} else {
self.hasRequestInFlight = YES;
__block isComplete = NO;
[self submitEvent:eventDictionary
eventKey:eventKey
success:^{
if (isComplete) {
return;
}
isComplete = YES;
DDLogDebug(@"%@ sendEvent succeeded: %@", self.tag, eventKey);
dispatch_async(self.serialQueue, ^{
self.hasRequestInFlight = NO;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
// Remove from queue.
[transaction removeObjectForKey:eventKey inCollection:kOWSAnalytics_EventsCollection];
}];
// Wait a second between network requests / retries.
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self tryToSyncEvents];
});
});
}
failure:^{
if (isComplete) {
return;
}
isComplete = YES;
DDLogError(@"%@ sendEvent failed: %@", self.tag, eventKey);
dispatch_async(self.serialQueue, ^{
self.hasRequestInFlight = NO;
// Wait a second between network requests / retries.
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self tryToSyncEvents];
});
});
}];
}
}
__block UIBackgroundTaskIdentifier task;
task = [UIApplication.sharedApplication beginBackgroundTaskWithExpirationHandler:^{
self.hasRequestInFlight = NO;
- (void)submitEvent:(NSDictionary *)eventDictionary
eventKey:(NSString *)eventKey
success:(void (^_Nonnull)())successBlock
failure:(void (^_Nonnull)())failureBlock
{
OWSAssert(eventDictionary);
OWSAssert(eventKey);
AssertOnDispatchQueue(self.serialQueue);
[UIApplication.sharedApplication endBackgroundTask:task];
}];
DDLogDebug(@"%@ submitting: %@", self.tag, eventKey);
// Until we integrate with an analytics platform, behave as though all event delivery succeeds.
dispatch_async(dispatch_get_main_queue(), ^{
self.hasRequestInFlight = NO;
__block UIBackgroundTaskIdentifier task;
task = [UIApplication.sharedApplication beginBackgroundTaskWithExpirationHandler:^{
failureBlock();
BOOL success = YES;
if (success) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
// Remove from queue.
[transaction removeObjectForKey:firstEventKey inCollection:kOWSAnalytics_EventsCollection];
}];
}
[UIApplication.sharedApplication endBackgroundTask:task];
}];
[UIApplication.sharedApplication endBackgroundTask:task];
// Until we integrate with an analytics platform, behave as though all event delivery succeeds.
dispatch_async(self.serialQueue, ^{
BOOL success = YES;
if (success) {
successBlock();
} else {
failureBlock();
}
// Wait a second between network requests / retries.
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self tryToSyncEvents];
});
});
[UIApplication.sharedApplication endBackgroundTask:task];
});
}
@ -233,18 +311,21 @@ const int kOWSAnalytics_DiscardFrequency = 0;
return (long)round(pow(10, floor(log10(value))));
}
- (void)addEvent:(NSString *)eventName async:(BOOL)async properties:(NSDictionary *)properties
- (void)addEvent:(NSString *)eventName severity:(OWSAnalyticsSeverity)severity properties:(NSDictionary *)properties
{
OWSAssert(eventName.length > 0);
BOOL isError = severity == OWSAnalyticsSeverityError;
BOOL isCritical = severity == OWSAnalyticsSeverityCritical;
uint32_t discardValue = arc4random_uniform(101);
if (discardValue < kOWSAnalytics_DiscardFrequency) {
if (!isError && !isCritical && discardValue < kOWSAnalytics_DiscardFrequency) {
DDLogVerbose(@"Discarding event: %@", eventName);
return;
}
#ifndef NO_SIGNAL_ANALYTICS
void (^writeEvent)() = ^{
void (^addEvent)() = ^{
// Add super properties.
NSMutableDictionary *eventProperties = (properties ? [properties mutableCopy] : [NSMutableDictionary new]);
[eventProperties addEntriesFromDictionary:self.eventSuperProperties];
@ -254,23 +335,29 @@ const int kOWSAnalytics_DiscardFrequency = 0;
NSString *eventKey = [NSUUID UUID].UUIDString;
DDLogDebug(@"%@ enqueuing event: %@", self.tag, eventKey);
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
const int kMaxQueuedEvents = 5000;
if ([transaction numberOfKeysInCollection:kOWSAnalytics_EventsCollection] > kMaxQueuedEvents) {
DDLogError(@"%@ Event queue overflow.", self.tag);
return;
}
[transaction setObject:eventDictionary forKey:eventKey inCollection:kOWSAnalytics_EventsCollection];
}];
[self tryToSyncEvents];
if (isCritical) {
// Critical events should not be serialized or enqueued - they should be submitted immediately.
[self sendEvent:eventDictionary eventKey:eventKey isCritical:YES];
} else {
// Add to queue.
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
const int kMaxQueuedEvents = 5000;
if ([transaction numberOfKeysInCollection:kOWSAnalytics_EventsCollection] > kMaxQueuedEvents) {
DDLogError(@"%@ Event queue overflow.", self.tag);
return;
}
[transaction setObject:eventDictionary forKey:eventKey inCollection:kOWSAnalytics_EventsCollection];
}];
[self tryToSyncEvents];
}
};
if (async) {
dispatch_async(self.serialQueue, writeEvent);
if ([self isSeverityAsync:severity]) {
dispatch_async(self.serialQueue, addEvent);
} else {
dispatch_sync(self.serialQueue, writeEvent);
dispatch_sync(self.serialQueue, addEvent);
}
#endif
}
@ -291,7 +378,6 @@ const int kOWSAnalytics_DiscardFrequency = 0;
line:(int)line
{
DDLogFlag logFlag;
BOOL async = YES;
switch (severity) {
case OWSAnalyticsSeverityInfo:
logFlag = DDLogFlagInfo;
@ -301,7 +387,6 @@ const int kOWSAnalytics_DiscardFrequency = 0;
break;
case OWSAnalyticsSeverityCritical:
logFlag = DDLogFlagError;
async = NO;
break;
default:
OWSAssert(0);
@ -312,17 +397,23 @@ const int kOWSAnalytics_DiscardFrequency = 0;
// Log the event.
NSString *logString = [NSString stringWithFormat:@"%s:%d %@", location, line, eventName];
if (!parameters) {
LOG_MAYBE(async, LOG_LEVEL_DEF, logFlag, 0, nil, location, @"%@", logString);
LOG_MAYBE([self isSeverityAsync:severity], LOG_LEVEL_DEF, logFlag, 0, nil, location, @"%@", logString);
} else {
LOG_MAYBE(async, LOG_LEVEL_DEF, logFlag, 0, nil, location, @"%@ %@", logString, parameters);
LOG_MAYBE(
[self isSeverityAsync:severity], LOG_LEVEL_DEF, logFlag, 0, nil, location, @"%@ %@", logString, parameters);
}
if (!async) {
if (![self isSeverityAsync:severity]) {
[DDLog flushLog];
}
NSMutableDictionary *eventProperties = (parameters ? [parameters mutableCopy] : [NSMutableDictionary new]);
eventProperties[@"event_location"] = [NSString stringWithFormat:@"%s:%d", location, line];
[self addEvent:eventName async:async properties:eventProperties];
[self addEvent:eventName severity:severity properties:eventProperties];
}
- (BOOL)isSeverityAsync:(OWSAnalyticsSeverity)severity
{
return severity == OWSAnalyticsSeverityCritical;
}
#pragma mark - Logging
@ -346,7 +437,7 @@ const int kOWSAnalytics_DiscardFrequency = 0;
[transaction objectForKey:kOWSAnalytics_KeyLaunchCompleteCount inCollection:kOWSAnalytics_Collection];
self.launchCompleteCount = @(oldLaunchCompleteCount.longValue);
}];
[TSStorageManager.sharedManager.newDatabaseConnection
[self.dbConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[transaction setObject:self.launchCount
forKey:kOWSAnalytics_KeyLaunchCount
@ -365,7 +456,7 @@ const int kOWSAnalytics_DiscardFrequency = 0;
self.launchCompleteCount = @(self.launchCompleteCount.longValue + 1);
[TSStorageManager.sharedManager.newDatabaseConnection
[self.dbConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[transaction setObject:self.launchCompleteCount
forKey:kOWSAnalytics_KeyLaunchCompleteCount

@ -0,0 +1,26 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
#ifdef DEBUG
#define AssertOnDispatchQueue(queue) \
{ \
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(10, 0)) { \
dispatch_assert_queue(queue); \
} else { \
_Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \
OWSAssert(dispatch_get_current_queue() == queue); \
_Pragma("clang diagnostic pop") \
} \
}
#else
#define AssertOnDispatchQueue(queue)
#endif
NS_ASSUME_NONNULL_END
Loading…
Cancel
Save