@ -16,17 +16,18 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
private var contentHandler : ( ( UNNotificationContent ) -> Void ) ?
private var contentHandler : ( ( UNNotificationContent ) -> Void ) ?
private var request : UNNotificationRequest ?
private var request : UNNotificationRequest ?
private var openGroupPollCancellable : AnyCancellable ?
private var openGroupPollCancellable : AnyCancellable ?
private var fileLogger: DDFileLogger ?
private var hasCompleted: Atomic < Bool > = Atomic ( false )
public static let isFromRemoteKey = " remote "
public static let isFromRemoteKey = " remote " // s t r i n g l i n t : d i s a b l e
public static let threadIdKey = " Signal.AppNotificationsUserInfoKey.threadId "
public static let threadIdKey = " Signal.AppNotificationsUserInfoKey.threadId " // s t r i n g l i n t : d i s a b l e
public static let threadVariantRaw = " Signal.AppNotificationsUserInfoKey.threadVariantRaw "
public static let threadVariantRaw = " Signal.AppNotificationsUserInfoKey.threadVariantRaw " // s t r i n g l i n t : d i s a b l e
public static let threadNotificationCounter = " Session.AppNotificationsUserInfoKey.threadNotificationCounter "
public static let threadNotificationCounter = " Session.AppNotificationsUserInfoKey.threadNotificationCounter " // s t r i n g l i n t : d i s a b l e
private static let callPreOfferLargeNotificationSupressionDuration : TimeInterval = 30
private static let callPreOfferLargeNotificationSupressionDuration : TimeInterval = 30
// MARK: D i d r e c e i v e a r e m o t e p u s h n o t i f i c a t i o n r e q u e s t
// MARK: D i d r e c e i v e a r e m o t e p u s h n o t i f i c a t i o n r e q u e s t
override public func didReceive ( _ request : UNNotificationRequest , withContentHandler contentHandler : @ escaping ( UNNotificationContent ) -> Void ) {
override public func didReceive ( _ request : UNNotificationRequest , withContentHandler contentHandler : @ escaping ( UNNotificationContent ) -> Void ) {
Log . info ( " didReceive called. " )
self . contentHandler = contentHandler
self . contentHandler = contentHandler
self . request = request
self . request = request
@ -83,7 +84,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
// J u s t l o g i f t h e n o t i f i c a t i o n w a s t o o l o n g ( a ~ 2 k m e s s a g e s h o u l d b e a b l e t o f i t s o
// J u s t l o g i f t h e n o t i f i c a t i o n w a s t o o l o n g ( a ~ 2 k m e s s a g e s h o u l d b e a b l e t o f i t s o
// t h e s e w i l l m o s t c o m m o n l y b e c a l l o r c o n f i g m e s s a g e s )
// t h e s e w i l l m o s t c o m m o n l y b e c a l l o r c o n f i g m e s s a g e s )
case . successTooLong :
case . successTooLong :
return SN Log( " [NotificationServiceExtension] Received too long notification for namespace: \( metadata . namespace ) . " , forceNSLog : true )
return Log. info ( " Received too long notification for namespace: \( metadata . namespace ) . " )
case . legacyForceSilent , . failureNoContent : return
case . legacyForceSilent , . failureNoContent : return
}
}
@ -95,8 +96,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
Storage . shared . write { db in
Storage . shared . write { db in
do {
do {
guard let processedMessage : ProcessedMessage = try Message . processRawReceivedMessageAsNotification ( db , data : data , metadata : metadata ) else {
guard let processedMessage : ProcessedMessage = try Message . processRawReceivedMessageAsNotification ( db , data : data , metadata : metadata ) else {
self . handleFailure ( for : notificationContent , error : . messageProcessing )
throw NotificationError . messageProcessing
return
}
}
switch processedMessage {
switch processedMessage {
@ -194,12 +194,29 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
}
}
}
}
catch {
catch {
if let error = error as ? MessageReceiverError , error . isRetryable {
// I f a n e r r o r o c c u r r e d w e w a n t t o r o l l b a c k t h e t r a n s a c t i o n ( b y t h r o w i n g ) a n d t h e n h a n d l e
// t h e e r r o r o u t s i d e o f t h e d a t a b a s e
let handleError = {
switch error {
switch error {
case . invalidGroupPublicKey , . noGroupKeyPair , . outdatedMessage : self . completeSilenty ( )
case MessageReceiverError . invalidGroupPublicKey , MessageReceiverError . noGroupKeyPair ,
default : self . handleFailure ( for : notificationContent , error : . messageHandling ( error ) )
MessageReceiverError . outdatedMessage :
self . completeSilenty ( )
case NotificationError . messageProcessing :
self . handleFailure ( for : notificationContent , error : . messageProcessing )
case let msgError as MessageReceiverError :
self . handleFailure ( for : notificationContent , error : . messageHandling ( msgError ) )
default : self . handleFailure ( for : notificationContent , error : . other ( error ) )
}
}
}
}
db . afterNextTransactionNested (
onCommit : { _ in handleError ( ) } ,
onRollback : { _ in handleError ( ) }
)
throw error
}
}
}
}
}
}
@ -215,7 +232,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
// t o p r o c e s s n e w m e s s a g e s .
// t o p r o c e s s n e w m e s s a g e s .
guard ! didPerformSetup else { return }
guard ! didPerformSetup else { return }
SN Log (" [NotificationServiceExtension] Performing setup" , forceNSLog : true )
Log.info (" Performing setup. " )
didPerformSetup = true
didPerformSetup = true
_ = AppVersion . sharedInstance ( )
_ = AppVersion . sharedInstance ( )
@ -224,26 +241,26 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
AppSetup . setupEnvironment (
AppSetup . setupEnvironment (
retrySetupIfDatabaseInvalid : true ,
retrySetupIfDatabaseInvalid : true ,
appSpecificBlock : { [ weak self ] in
appSpecificBlock : {
Log . setup ( with : Logger (
primaryPrefix : " NotificationServiceExtension " , // s t r i n g l i n t : d i s a b l e
customDirectory : " \( OWSFileSystem . appSharedDataDirectoryPath ( ) ) /Logs/NotificationExtension " , // s t r i n g l i n t : d i s a b l e
forceNSLog : true
) )
SessionEnvironment . shared ? . notificationsManager . mutate {
SessionEnvironment . shared ? . notificationsManager . mutate {
$0 = NSENotificationPresenter ( )
$0 = NSENotificationPresenter ( )
}
}
// A d d t h e f i l e l o g g e r
// S e t u p L i b S e s s i o n
let logFileManager : DDLogFileManagerDefault = DDLogFileManagerDefault (
LibSession . addLogger ( )
logsDirectory : " \( OWSFileSystem . appSharedDataDirectoryPath ( ) ) /Logs/NotificationExtension " // s t r i n g l i n t : d i s a b l e
LibSession . createNetworkIfNeeded ( )
)
let fileLogger : DDFileLogger = DDFileLogger ( logFileManager : logFileManager )
fileLogger . rollingFrequency = kDayInterval // R e f r e s h e v e r y d a y
fileLogger . logFileManager . maximumNumberOfLogFiles = 3 // S a v e 3 d a y s ' l o g f i l e s
DDLog . add ( fileLogger )
self ? . fileLogger = fileLogger
} ,
} ,
migrationsCompletion : { [ weak self ] result , needsConfigSync in
migrationsCompletion : { [ weak self ] result , needsConfigSync in
switch result {
switch result {
// O n l y ' N S L o g ' w o r k s i n t h e e x t e n s i o n - v i e w a b l e v i a C o n s o l e . a p p
// O n l y ' N S L o g ' w o r k s i n t h e e x t e n s i o n - v i e w a b l e v i a C o n s o l e . a p p
case . failure ( let error ) :
case . failure ( let error ) :
SN Log (" [NotificationServiceExtension] Failed to complete migrations: \( error ) ", forceNSLog : true )
Log . error ( " Failed to complete migrations: \( error ) . " )
self ? . completeSilenty ( )
self ? . completeSilenty ( )
case . success :
case . success :
@ -253,7 +270,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
// s o i t i s p o s s i b l e t h a t c o u l d c h a n g e i n t h e f u t u r e . I f i t d o e s , d o n o t h i n g
// s o i t i s p o s s i b l e t h a t c o u l d c h a n g e i n t h e f u t u r e . I f i t d o e s , d o n o t h i n g
// a n d d o n ' t d i s t u r b t h e u s e r . M e s s a g e s w i l l b e p r o c e s s e d w h e n t h e y o p e n t h e a p p .
// a n d d o n ' t d i s t u r b t h e u s e r . M e s s a g e s w i l l b e p r o c e s s e d w h e n t h e y o p e n t h e a p p .
guard Storage . shared [ . isReadyForAppExtensions ] else {
guard Storage . shared [ . isReadyForAppExtensions ] else {
SN Log (" [NotificationServiceExtension] Not ready for extensions" , forceNSLog : true )
Log.error (" Not ready for extensions. " )
self ? . completeSilenty ( )
self ? . completeSilenty ( )
return
return
}
}
@ -289,7 +306,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
// A p p i s n ' t r e a d y u n t i l s t o r a g e i s r e a d y A N D a l l v e r s i o n m i g r a t i o n s a r e c o m p l e t e .
// A p p i s n ' t r e a d y u n t i l s t o r a g e i s r e a d y A N D a l l v e r s i o n m i g r a t i o n s a r e c o m p l e t e .
guard Storage . shared . isValid && migrationsCompleted else {
guard Storage . shared . isValid && migrationsCompleted else {
SN Log (" [NotificationServiceExtension] Storage invalid" , forceNSLog : true )
Log.error (" Storage invalid. " )
self . completeSilenty ( )
self . completeSilenty ( )
return
return
}
}
@ -305,14 +322,23 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
override public func serviceExtensionTimeWillExpire ( ) {
override public func serviceExtensionTimeWillExpire ( ) {
// C a l l e d j u s t b e f o r e t h e e x t e n s i o n w i l l b e t e r m i n a t e d b y t h e s y s t e m .
// C a l l e d j u s t b e f o r e t h e e x t e n s i o n w i l l b e t e r m i n a t e d b y t h e s y s t e m .
// U s e t h i s a s a n o p p o r t u n i t y t o d e l i v e r y o u r " b e s t a t t e m p t " a t m o d i f i e d c o n t e n t , o t h e r w i s e t h e o r i g i n a l p u s h p a y l o a d w i l l b e u s e d .
// U s e t h i s a s a n o p p o r t u n i t y t o d e l i v e r y o u r " b e s t a t t e m p t " a t m o d i f i e d c o n t e n t , o t h e r w i s e t h e o r i g i n a l p u s h p a y l o a d w i l l b e u s e d .
SN Log (" [NotificationServiceExtension] Execution time expired" , forceNSLog : true )
Log.warn (" Execution time expired. " )
openGroupPollCancellable ? . cancel ( )
openGroupPollCancellable ? . cancel ( )
completeSilenty ( )
completeSilenty ( )
}
}
private func completeSilenty ( ) {
private func completeSilenty ( ) {
SNLog ( " [NotificationServiceExtension] Complete silently " , forceNSLog : true )
// E n s u r e w e o n ' y r u n t h i s o n c e
DDLog . flushLog ( )
guard
hasCompleted . mutate ( { hasCompleted in
let wasCompleted : Bool = hasCompleted
hasCompleted = true
return wasCompleted
} ) = = false
else { return }
Log . info ( " Complete silently. " )
Log . flush ( )
let silentContent : UNMutableNotificationContent = UNMutableNotificationContent ( )
let silentContent : UNMutableNotificationContent = UNMutableNotificationContent ( )
silentContent . badge = Storage . shared
silentContent . badge = Storage . shared
. read { db in try Interaction . fetchUnreadCount ( db ) }
. read { db in try Interaction . fetchUnreadCount ( db ) }
@ -329,18 +355,18 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
guard let caller : String = callMessage . sender , let timestamp = callMessage . sentTimestamp else { return }
guard let caller : String = callMessage . sender , let timestamp = callMessage . sentTimestamp else { return }
let payload : JSON = [
let payload : JSON = [
" uuid " : callMessage . uuid ,
" uuid " : callMessage . uuid , // s t r i n g l i n t : d i s a b l e
" caller " : caller ,
" caller " : caller , // s t r i n g l i n t : d i s a b l e
" timestamp " : timestamp
" timestamp " : timestamp // s t r i n g l i n t : d i s a b l e
]
]
CXProvider . reportNewIncomingVoIPPushPayload ( payload ) { error in
CXProvider . reportNewIncomingVoIPPushPayload ( payload ) { error in
if let error = error {
if let error = error {
self . handleFailureForVoIP ( db , for : callMessage )
self . handleFailureForVoIP ( db , for : callMessage )
SN Log (" [NotificationServiceExtension] Failed to notify main app of call message: \( error ) ", forceNSLog : true )
Log.error (" Failed to notify main app of call message: \( error ) ." )
}
}
else {
else {
SN Log (" [NotificationServiceExtension] Successfully notified main app of call message." , forceNSLog : true )
Log.info (" Successfully notified main app of call message." )
UserDefaults . sharedLokiProject ? [ . lastCallPreOffer ] = Date ( )
UserDefaults . sharedLokiProject ? [ . lastCallPreOffer ] = Date ( )
self . completeSilenty ( )
self . completeSilenty ( )
}
}
@ -373,18 +399,18 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
UNUserNotificationCenter . current ( ) . add ( request ) { error in
UNUserNotificationCenter . current ( ) . add ( request ) { error in
if let error = error {
if let error = error {
SN Log (" [NotificationServiceExtension] Failed to add notification request due to error: \( error ) ", forceNSLog : true )
Log.error (" Failed to add notification request due to error: \( error ) ." )
}
}
semaphore . signal ( )
semaphore . signal ( )
}
}
semaphore . wait ( )
semaphore . wait ( )
SN Log (" [NotificationServiceExtension] Add remote notification request" , forceNSLog : true )
Log.info (" Add remote notification request. " )
DD Log. flush Log ( )
Log. flush ( )
}
}
private func handleFailure ( for content : UNMutableNotificationContent , error : NotificationError ) {
private func handleFailure ( for content : UNMutableNotificationContent , error : NotificationError ) {
SN Log (" [NotificationServiceExtension] Show generic failure message due to error: \( error ) ", forceNSLog : true )
Log.error (" Show generic failure message due to error: \( error ) ." )
DD Log. flush Log ( )
Log. flush ( )
Storage . suspendDatabaseAccess ( )
Storage . suspendDatabaseAccess ( )
LibSession . closeNetworkConnections ( )
LibSession . closeNetworkConnections ( )