blob: d864a5b16f8628d296bc136fe3d80a76cfb460cb [file] [log] [blame]
// Copyright 2021 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/app/enterprise_app_agent.h"
#import "base/check.h"
#import "base/memory/raw_ptr.h"
#import "components/policy/core/common/cloud/cloud_policy_client.h"
#import "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
#import "components/policy/core/common/policy_namespace.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/app/application_delegate/startup_information.h"
#import "ios/chrome/app/enterprise_loading_screen_view_controller.h"
#import "ios/chrome/app/tests_hook.h"
#import "ios/chrome/browser/policy/model/browser_policy_connector_ios.h"
#import "ios/chrome/browser/policy/model/chrome_browser_cloud_management_controller_ios.h"
#import "ios/chrome/browser/policy/model/chrome_browser_cloud_management_controller_observer_bridge.h"
#import "ios/chrome/browser/policy/model/cloud_policy_client_observer_bridge.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
namespace {
constexpr CGFloat kTimeout = 30;
} // namespace
@interface EnterpriseAppAgent () <
ChromeBrowserCloudManagementControllerObserver,
CloudPolicyClientObserver,
SceneStateObserver> {
std::unique_ptr<ChromeBrowserCloudManagementControllerObserverBridge>
_cloudManagementControllerObserver;
std::unique_ptr<CloudPolicyClientObserverBridge> _cloudPolicyClientObserver;
raw_ptr<BrowserPolicyConnectorIOS> _policyConnector;
}
// The app state for the app.
@property(nonatomic, weak, readonly) AppState* appState;
// Browser policy connector for iOS.
@property(nonatomic, assign) raw_ptr<BrowserPolicyConnectorIOS> policyConnector;
// YES if enterprise launch screen has been dismissed.
@property(nonatomic, assign) BOOL launchScreenDismissed;
@end
@implementation EnterpriseAppAgent
- (void)dealloc {
for (SceneState* scene in _appState.connectedScenes) {
[scene removeObserver:self];
}
[_appState removeObserver:self];
}
- (void)setAppState:(AppState*)appState {
// This should only be called once!
DCHECK(!_appState);
_appState = appState;
[appState addObserver:self];
for (SceneState* scene in appState.connectedScenes) {
[scene addObserver:self];
}
}
#pragma mark - AppStateObserver
- (void)appState:(AppState*)appState sceneConnected:(SceneState*)sceneState {
[sceneState addObserver:self];
}
- (void)appState:(AppState*)appState
didTransitionFromInitStage:(InitStage)previousInitStage {
if (appState.initStage == InitStageEnterprise) {
if ([self shouldShowEnterpriseLoadScreen]) {
_cloudManagementControllerObserver = std::make_unique<
ChromeBrowserCloudManagementControllerObserverBridge>(
self,
self.policyConnector->chrome_browser_cloud_management_controller());
policy::CloudPolicyClient* client =
self.policyConnector->machine_level_user_cloud_policy_manager()
->core()
->client();
_cloudPolicyClientObserver =
std::make_unique<CloudPolicyClientObserverBridge>(self, client);
self.launchScreenDismissed = NO;
for (SceneState* scene in appState.connectedScenes) {
if (scene.activationLevel > SceneActivationLevelBackground) {
[self showUIInScene:scene];
}
}
// Ensure to never stay stuck on enterprise launch screen.
__weak EnterpriseAppAgent* weakSelf = self;
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kTimeout * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
if (!weakSelf.launchScreenDismissed) {
[weakSelf cloudPolicyDidError:nullptr];
}
});
} else {
[self.appState queueTransitionToNextInitStage];
}
}
if (previousInitStage == InitStageEnterprise) {
// Nothing left to do; clean up.
_cloudManagementControllerObserver = nullptr;
_cloudPolicyClientObserver = nullptr;
// Let the following line at the end of the block.
[self.appState removeAgent:self];
}
}
#pragma mark - SceneStateObserver
- (void)sceneState:(SceneState*)sceneState
transitionedToActivationLevel:(SceneActivationLevel)level {
if (self.appState.initStage == InitStageEnterprise &&
level > SceneActivationLevelBackground) {
[self showUIInScene:sceneState];
}
}
#pragma mark - ChromeBrowserCloudManagementControllerObserverBridge
- (void)policyRegistrationDidCompleteSuccessfuly:(BOOL)succeeded {
if (!succeeded && !self.launchScreenDismissed) {
self.launchScreenDismissed = YES;
[self.appState queueTransitionToNextInitStage];
}
}
#pragma mark - CloudPolicyClientObserverBridge
- (void)cloudPolicyWasFetched:(policy::CloudPolicyClient*)client {
if (!self.launchScreenDismissed) {
self.launchScreenDismissed = YES;
[self.appState queueTransitionToNextInitStage];
}
}
- (void)cloudPolicyDidError:(policy::CloudPolicyClient*)client {
if (!self.launchScreenDismissed) {
self.launchScreenDismissed = YES;
[self.appState queueTransitionToNextInitStage];
}
}
- (void)cloudPolicyRegistrationChanged:(policy::CloudPolicyClient*)client {
// Ignored as for initialization, only registration and fetch completion
// results are needed.
}
#pragma mark - private
- (void)showUIInScene:(SceneState*)sceneState {
if ([sceneState.window.rootViewController
isKindOfClass:[EnterpriseLoadScreenViewController class]]) {
return;
}
sceneState.window.rootViewController =
[[EnterpriseLoadScreenViewController alloc] init];
[sceneState.window makeKeyAndVisible];
}
- (BOOL)shouldShowEnterpriseLoadScreen {
self.policyConnector = GetApplicationContext()->GetBrowserPolicyConnector();
// `policyConnector` is nullptr if policy is not enabled.
if (!self.policyConnector) {
return NO;
}
// `machineLevelUserCloudPolicyManager` is nullptr if the DM token needed
// for fetch is explicitly invalid or if enrollment tokens and DM token are
// empty.
policy::MachineLevelUserCloudPolicyManager*
machineLevelUserCloudPolicyManager =
self.policyConnector->machine_level_user_cloud_policy_manager();
return self.appState.startupInformation.isFirstRun &&
self.policyConnector->chrome_browser_cloud_management_controller()
->IsEnabled() &&
machineLevelUserCloudPolicyManager &&
!machineLevelUserCloudPolicyManager->IsFirstPolicyLoadComplete(
policy::POLICY_DOMAIN_CHROME);
}
@end