blob: 33bd047712eba1f55c388ba976a3c1c85b26faa1 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/push_notification/push_notification_delegate.h"
#import "base/check.h"
#import "base/files/file_path.h"
#import "base/metrics/histogram_functions.h"
#import "base/strings/sys_string_conversions.h"
#import "base/time/time.h"
#import "base/timer/timer.h"
#import "base/values.h"
#import "components/prefs/pref_service.h"
#import "ios/chrome/browser/push_notification/push_notification_client_manager.h"
#import "ios/chrome/browser/push_notification/push_notification_configuration.h"
#import "ios/chrome/browser/push_notification/push_notification_delegate.h"
#import "ios/chrome/browser/push_notification/push_notification_service.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser_state/browser_state_info_cache.h"
#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state_manager.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// The time range's expected min and max values for custom histograms.
constexpr base::TimeDelta kTimeRangeIncomingNotificationHistogramMin =
base::Milliseconds(1);
constexpr base::TimeDelta kTimeRangeIncomingNotificationHistogramMax =
base::Seconds(30);
// Number of buckets for the time range histograms.
constexpr int kTimeRangeHistogramBucketCount = 30;
// This function creates a dictionary that maps signed-in user's GAIA IDs to a
// map of each user's preferences for each push notification enabled feature.
GaiaIdToPushNotificationPreferenceMap*
GaiaIdToPushNotificationPreferenceMapFromCache(
BrowserStateInfoCache* info_cache) {
size_t number_of_browser_states = info_cache->GetNumberOfBrowserStates();
NSMutableDictionary* account_preference_map =
[[NSMutableDictionary alloc] init];
for (size_t i = 0; i < number_of_browser_states; i++) {
NSString* gaia_id =
base::SysUTF8ToNSString(info_cache->GetGAIAIdOfBrowserStateAtIndex(i));
if (!gaia_id.length) {
continue;
}
base::FilePath path = info_cache->GetPathOfBrowserStateAtIndex(i);
PrefService* pref_service = GetApplicationContext()
->GetChromeBrowserStateManager()
->GetBrowserState(path)
->GetPrefs();
NSMutableDictionary<NSString*, NSNumber*>* preference_map =
[[NSMutableDictionary alloc] init];
const base::Value::Dict& permissions =
pref_service->GetDict(prefs::kFeaturePushNotificationPermissions);
for (const auto pair : permissions) {
preference_map[base::SysUTF8ToNSString(pair.first)] =
[NSNumber numberWithBool:pair.second.GetBool()];
}
account_preference_map[gaia_id] = preference_map;
}
return account_preference_map;
}
} // anonymous namespace
@implementation PushNotificationDelegate
- (instancetype)initWithAppState:(AppState*)appState {
[appState addObserver:self];
return self;
}
#pragma mark - UNUserNotificationCenterDelegate -
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
didReceiveNotificationResponse:(UNNotificationResponse*)response
withCompletionHandler:(void (^)(void))completionHandler {
// This method is invoked by iOS to process the user's response to a delivered
// notification.
auto* clientManager = GetApplicationContext()
->GetPushNotificationService()
->GetPushNotificationClientManager();
DCHECK(clientManager);
clientManager->HandleNotificationInteraction(response);
if (completionHandler) {
completionHandler();
}
}
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
willPresentNotification:(UNNotification*)notification
withCompletionHandler:
(void (^)(UNNotificationPresentationOptions options))
completionHandler {
// This method is invoked by iOS to process a notification that arrived while
// the app was running in the foreground.
auto* clientManager = GetApplicationContext()
->GetPushNotificationService()
->GetPushNotificationClientManager();
DCHECK(clientManager);
clientManager->HandleNotificationReception(
notification.request.content.userInfo);
if (completionHandler) {
completionHandler(UNNotificationPresentationOptionBanner);
}
}
#pragma mark - PushNotificationDelegate
- (UIBackgroundFetchResult)applicationWillProcessIncomingRemoteNotification:
(NSDictionary*)userInfo {
double incomingNotificationTime = base::Time::Now().ToDoubleT();
auto* clientManager = GetApplicationContext()
->GetPushNotificationService()
->GetPushNotificationClientManager();
DCHECK(clientManager);
UIBackgroundFetchResult result =
clientManager->HandleNotificationReception(userInfo);
double processingTime =
incomingNotificationTime - base::Time::Now().ToDoubleT();
UmaHistogramCustomTimes(
"IOS.PushNotification.IncomingNotificationProcessingTime",
base::Milliseconds(processingTime),
kTimeRangeIncomingNotificationHistogramMin,
kTimeRangeIncomingNotificationHistogramMax,
kTimeRangeHistogramBucketCount);
return result;
}
- (void)applicationDidRegisterWithAPNS:(NSData*)deviceToken {
BrowserStateInfoCache* infoCache = GetApplicationContext()
->GetChromeBrowserStateManager()
->GetBrowserStateInfoCache();
GaiaIdToPushNotificationPreferenceMap* accountPreferenceMap =
GaiaIdToPushNotificationPreferenceMapFromCache(infoCache);
// Return early if no accounts are signed into Chrome.
if (!accountPreferenceMap.count) {
return;
}
PushNotificationService* notificationService =
GetApplicationContext()->GetPushNotificationService();
// Registers Chrome's PushNotificationClients' Actionable Notifications with
// iOS.
notificationService->GetPushNotificationClientManager()
->RegisterActionableNotifications();
PushNotificationConfiguration* config =
[[PushNotificationConfiguration alloc] init];
config.accountIDs = accountPreferenceMap.allKeys;
config.preferenceMap = accountPreferenceMap;
config.deviceToken = deviceToken;
config.ssoService = GetApplicationContext()->GetSSOService();
notificationService->RegisterDevice(config, ^(NSError* error) {
if (error) {
base::UmaHistogramBoolean("IOS.PushNotification.ChimeDeviceRegistration",
false);
} else {
base::UmaHistogramBoolean("IOS.PushNotification.ChimeDeviceRegistration",
true);
}
});
}
#pragma mark - AppStateObserver
- (void)appState:(AppState*)appState
sceneDidBecomeActive:(SceneState*)sceneState {
if (appState.initStage < InitStageFinal) {
return;
}
PushNotificationClientManager* clientManager =
GetApplicationContext()
->GetPushNotificationService()
->GetPushNotificationClientManager();
DCHECK(clientManager);
clientManager->OnSceneActiveForegroundBrowserReady();
}
@end