[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Yeunjoo Choi | 5097e9c | 2021-02-25 08:46:52 | [diff] [blame] | 5 | #include "chrome/browser/ash/settings/session_manager_operation.h" |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 6 | |
dcheng | 7c802f0 | 2015-12-31 16:09:55 | [diff] [blame] | 7 | #include <utility> |
| 8 | |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 9 | #include "base/bind.h" |
danakj | db9ae794 | 2020-11-11 16:01:35 | [diff] [blame] | 10 | #include "base/callback_helpers.h" |
[email protected] | 5799981 | 2013-02-24 05:40:52 | [diff] [blame] | 11 | #include "base/files/file_path.h" |
Hans Wennborg | f6ad69c | 2020-06-18 18:02:32 | [diff] [blame] | 12 | #include "base/logging.h" |
Gabriel Charette | 5ff87ce | 2017-05-16 18:03:45 | [diff] [blame] | 13 | #include "base/sequenced_task_runner.h" |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 14 | #include "base/stl_util.h" |
Gabriel Charette | 44db142 | 2018-08-06 11:19:33 | [diff] [blame] | 15 | #include "base/task/post_task.h" |
| 16 | #include "base/task/task_traits.h" |
Gabriel Charette | 05503913 | 2020-02-26 23:02:06 | [diff] [blame] | 17 | #include "base/task/thread_pool.h" |
Francois Doray | 11b25d7ceb | 2018-10-05 20:34:11 | [diff] [blame] | 18 | #include "chrome/browser/net/nss_context.h" |
ygorshenin | 39e3678 | 2014-08-29 13:09:51 | [diff] [blame] | 19 | #include "components/ownership/owner_key_util.h" |
[email protected] | 76b4b15 | 2013-12-08 21:10:04 | [diff] [blame] | 20 | #include "components/policy/core/common/cloud/cloud_policy_constants.h" |
Roman Sorokin | 1b1677d07 | 2017-10-16 12:27:48 | [diff] [blame] | 21 | #include "components/policy/proto/chrome_device_policy.pb.h" |
brettw | 39d6ba4 | 2016-08-24 16:56:38 | [diff] [blame] | 22 | #include "components/policy/proto/device_management_backend.pb.h" |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 23 | #include "crypto/rsa_private_key.h" |
| 24 | #include "crypto/signature_creator.h" |
| 25 | |
igorcov | f305a94 | 2017-04-28 15:16:23 | [diff] [blame] | 26 | using RetrievePolicyResponseType = |
| 27 | chromeos::SessionManagerClient::RetrievePolicyResponseType; |
ygorshenin | 39e3678 | 2014-08-29 13:09:51 | [diff] [blame] | 28 | using ownership::OwnerKeyUtil; |
| 29 | using ownership::PublicKey; |
| 30 | |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 31 | namespace em = enterprise_management; |
| 32 | |
Yeunjoo Choi | d461f87 | 2021-03-11 06:52:19 | [diff] [blame^] | 33 | namespace ash { |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 34 | |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 35 | SessionManagerOperation::SessionManagerOperation(Callback callback) |
| 36 | : callback_(std::move(callback)) {} |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 37 | |
| 38 | SessionManagerOperation::~SessionManagerOperation() {} |
| 39 | |
| 40 | void SessionManagerOperation::Start( |
Yeunjoo Choi | d461f87 | 2021-03-11 06:52:19 | [diff] [blame^] | 41 | chromeos::SessionManagerClient* session_manager_client, |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 42 | scoped_refptr<OwnerKeyUtil> owner_key_util, |
[email protected] | ea5a778a | 2014-06-16 12:28:33 | [diff] [blame] | 43 | scoped_refptr<PublicKey> public_key) { |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 44 | session_manager_client_ = session_manager_client; |
| 45 | owner_key_util_ = owner_key_util; |
[email protected] | ea5a778a | 2014-06-16 12:28:33 | [diff] [blame] | 46 | public_key_ = public_key; |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 47 | Run(); |
| 48 | } |
| 49 | |
| 50 | void SessionManagerOperation::RestartLoad(bool key_changed) { |
| 51 | if (key_changed) |
emaxx | 8aaab6c | 2017-03-07 14:57:33 | [diff] [blame] | 52 | public_key_ = nullptr; |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 53 | |
| 54 | if (!is_loading_) |
| 55 | return; |
| 56 | |
| 57 | // Abort previous load operations. |
| 58 | weak_factory_.InvalidateWeakPtrs(); |
[email protected] | 594401ea | 2013-10-29 22:19:06 | [diff] [blame] | 59 | // Mark as not loading to start loading again. |
| 60 | is_loading_ = false; |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 61 | StartLoading(); |
| 62 | } |
| 63 | |
| 64 | void SessionManagerOperation::StartLoading() { |
[email protected] | 594401ea | 2013-10-29 22:19:06 | [diff] [blame] | 65 | if (is_loading_) |
| 66 | return; |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 67 | is_loading_ = true; |
tnagel | 8d2791ec | 2016-11-18 10:43:13 | [diff] [blame] | 68 | if (cloud_validations_) { |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 69 | EnsurePublicKey( |
| 70 | base::BindOnce(&SessionManagerOperation::RetrieveDeviceSettings, |
| 71 | weak_factory_.GetWeakPtr())); |
tnagel | 8d2791ec | 2016-11-18 10:43:13 | [diff] [blame] | 72 | } else { |
| 73 | RetrieveDeviceSettings(); |
| 74 | } |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 75 | } |
| 76 | |
poromov | afc9d60e | 2017-02-28 16:38:10 | [diff] [blame] | 77 | void SessionManagerOperation::LoadImmediately() { |
Roman Sorokin | 716380f9 | 2018-04-19 10:10:45 | [diff] [blame] | 78 | if (cloud_validations_) { |
| 79 | StorePublicKey( |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 80 | base::BindOnce(&SessionManagerOperation::BlockingRetrieveDeviceSettings, |
| 81 | weak_factory_.GetWeakPtr()), |
Roman Sorokin | 716380f9 | 2018-04-19 10:10:45 | [diff] [blame] | 82 | LoadPublicKey(owner_key_util_, public_key_)); |
| 83 | } else { |
| 84 | BlockingRetrieveDeviceSettings(); |
| 85 | } |
poromov | afc9d60e | 2017-02-28 16:38:10 | [diff] [blame] | 86 | } |
| 87 | |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 88 | void SessionManagerOperation::ReportResult( |
| 89 | DeviceSettingsService::Status status) { |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 90 | std::move(callback_).Run(this, status); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 91 | } |
| 92 | |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 93 | void SessionManagerOperation::EnsurePublicKey(base::OnceClosure callback) { |
emaxx | 8aaab6c | 2017-03-07 14:57:33 | [diff] [blame] | 94 | if (force_key_load_ || !public_key_ || !public_key_->is_loaded()) { |
Gabriel Charette | 05503913 | 2020-02-26 23:02:06 | [diff] [blame] | 95 | base::ThreadPool::PostTaskAndReplyWithResult( |
Francois Doray | 11b25d7ceb | 2018-10-05 20:34:11 | [diff] [blame] | 96 | FROM_HERE, |
Gabriel Charette | 05503913 | 2020-02-26 23:02:06 | [diff] [blame] | 97 | {base::MayBlock(), base::TaskPriority::USER_VISIBLE, |
Francois Doray | 11b25d7ceb | 2018-10-05 20:34:11 | [diff] [blame] | 98 | base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, |
| 99 | base::BindOnce(&SessionManagerOperation::LoadPublicKey, owner_key_util_, |
| 100 | force_key_load_ ? nullptr : public_key_), |
| 101 | base::BindOnce(&SessionManagerOperation::StorePublicKey, |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 102 | weak_factory_.GetWeakPtr(), std::move(callback))); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 103 | } else { |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 104 | std::move(callback).Run(); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 105 | } |
| 106 | } |
| 107 | |
| 108 | // static |
[email protected] | ea5a778a | 2014-06-16 12:28:33 | [diff] [blame] | 109 | scoped_refptr<PublicKey> SessionManagerOperation::LoadPublicKey( |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 110 | scoped_refptr<OwnerKeyUtil> util, |
[email protected] | ea5a778a | 2014-06-16 12:28:33 | [diff] [blame] | 111 | scoped_refptr<PublicKey> current_key) { |
| 112 | scoped_refptr<PublicKey> public_key(new PublicKey()); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 113 | |
[email protected] | ea5a778a | 2014-06-16 12:28:33 | [diff] [blame] | 114 | // Keep already-existing public key. |
emaxx | 8aaab6c | 2017-03-07 14:57:33 | [diff] [blame] | 115 | if (current_key && current_key->is_loaded()) { |
[email protected] | ea5a778a | 2014-06-16 12:28:33 | [diff] [blame] | 116 | public_key->data() = current_key->data(); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 117 | } |
[email protected] | ea5a778a | 2014-06-16 12:28:33 | [diff] [blame] | 118 | if (!public_key->is_loaded() && util->IsPublicKeyPresent()) { |
| 119 | if (!util->ImportPublicKey(&public_key->data())) |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 120 | LOG(ERROR) << "Failed to load public owner key."; |
| 121 | } |
| 122 | |
[email protected] | ea5a778a | 2014-06-16 12:28:33 | [diff] [blame] | 123 | return public_key; |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 124 | } |
| 125 | |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 126 | void SessionManagerOperation::StorePublicKey(base::OnceClosure callback, |
[email protected] | ea5a778a | 2014-06-16 12:28:33 | [diff] [blame] | 127 | scoped_refptr<PublicKey> new_key) { |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 128 | force_key_load_ = false; |
[email protected] | ea5a778a | 2014-06-16 12:28:33 | [diff] [blame] | 129 | public_key_ = new_key; |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 130 | |
emaxx | 8aaab6c | 2017-03-07 14:57:33 | [diff] [blame] | 131 | if (!public_key_ || !public_key_->is_loaded()) { |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 132 | ReportResult(DeviceSettingsService::STORE_KEY_UNAVAILABLE); |
| 133 | return; |
| 134 | } |
| 135 | |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 136 | std::move(callback).Run(); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 137 | } |
| 138 | |
| 139 | void SessionManagerOperation::RetrieveDeviceSettings() { |
| 140 | session_manager_client()->RetrieveDevicePolicy( |
Hidehiko Abe | 86f38b0d | 2017-10-26 02:24:01 | [diff] [blame] | 141 | base::BindOnce(&SessionManagerOperation::ValidateDeviceSettings, |
| 142 | weak_factory_.GetWeakPtr())); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 143 | } |
| 144 | |
poromov | afc9d60e | 2017-02-28 16:38:10 | [diff] [blame] | 145 | void SessionManagerOperation::BlockingRetrieveDeviceSettings() { |
igorcov | f305a94 | 2017-04-28 15:16:23 | [diff] [blame] | 146 | std::string policy_blob; |
| 147 | RetrievePolicyResponseType response = |
| 148 | session_manager_client()->BlockingRetrieveDevicePolicy(&policy_blob); |
Hidehiko Abe | 86f38b0d | 2017-10-26 02:24:01 | [diff] [blame] | 149 | ValidateDeviceSettings(response, policy_blob); |
poromov | afc9d60e | 2017-02-28 16:38:10 | [diff] [blame] | 150 | } |
| 151 | |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 152 | void SessionManagerOperation::ValidateDeviceSettings( |
Hidehiko Abe | 86f38b0d | 2017-10-26 02:24:01 | [diff] [blame] | 153 | RetrievePolicyResponseType response_type, |
| 154 | const std::string& policy_blob) { |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 155 | if (policy_blob.empty()) { |
| 156 | ReportResult(DeviceSettingsService::STORE_NO_POLICY); |
| 157 | return; |
| 158 | } |
| 159 | |
tnagel | efffaf0 | 2017-06-02 10:00:46 | [diff] [blame] | 160 | std::unique_ptr<em::PolicyFetchResponse> policy = |
Jinho Bang | 341ce5b0 | 2018-01-17 22:46:03 | [diff] [blame] | 161 | std::make_unique<em::PolicyFetchResponse>(); |
igorcov | f305a94 | 2017-04-28 15:16:23 | [diff] [blame] | 162 | if (!policy->ParseFromString(policy_blob) || !policy->IsInitialized()) { |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 163 | ReportResult(DeviceSettingsService::STORE_INVALID_POLICY); |
| 164 | return; |
| 165 | } |
| 166 | |
[email protected] | 6a6dd9d | 2013-10-19 18:16:59 | [diff] [blame] | 167 | scoped_refptr<base::SequencedTaskRunner> background_task_runner = |
Gabriel Charette | 05503913 | 2020-02-26 23:02:06 | [diff] [blame] | 168 | base::ThreadPool::CreateSequencedTaskRunner( |
| 169 | {base::MayBlock(), base::TaskPriority::USER_BLOCKING, |
Francois Doray | 828fb591 | 2017-08-01 15:25:15 | [diff] [blame] | 170 | base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); |
[email protected] | 6a6dd9d | 2013-10-19 18:16:59 | [diff] [blame] | 171 | |
poromov | afc9d60e | 2017-02-28 16:38:10 | [diff] [blame] | 172 | std::unique_ptr<policy::DeviceCloudPolicyValidator> validator = |
Lutz Justen | bc2ee55 | 2018-04-12 13:27:38 | [diff] [blame] | 173 | std::make_unique<policy::DeviceCloudPolicyValidator>( |
| 174 | std::move(policy), background_task_runner); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 175 | |
tnagel | 8d2791ec | 2016-11-18 10:43:13 | [diff] [blame] | 176 | if (cloud_validations_) { |
| 177 | // Policy auto-generated by session manager doesn't include a timestamp, so |
emaxx | 5a15985 | 2017-04-20 10:25:48 | [diff] [blame] | 178 | // the timestamp shouldn't be verified in that case. Note that the timestamp |
tnagel | 8d2791ec | 2016-11-18 10:43:13 | [diff] [blame] | 179 | // is still verified during enrollment and when a new policy is fetched from |
| 180 | // the server. |
| 181 | // |
| 182 | // The two *_NOT_REQUIRED options are necessary because both the DM token |
| 183 | // and the device id are empty for a user logging in on an actual Chrome OS |
| 184 | // device that is not enterprise-managed. Note for devs: The strings are not |
| 185 | // empty when you test Chrome with target_os = "chromeos" on Linux! |
| 186 | validator->ValidateAgainstCurrentPolicy( |
| 187 | policy_data_.get(), |
| 188 | policy::CloudPolicyValidatorBase::TIMESTAMP_NOT_VALIDATED, |
| 189 | policy::CloudPolicyValidatorBase::DM_TOKEN_NOT_REQUIRED, |
| 190 | policy::CloudPolicyValidatorBase::DEVICE_ID_NOT_REQUIRED); |
| 191 | |
| 192 | // We don't check the DMServer verification key below, because the signing |
| 193 | // key is validated when it is installed. |
| 194 | validator->ValidateSignature(public_key_->as_string()); |
| 195 | } |
| 196 | |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 197 | validator->ValidatePolicyType(policy::dm_protocol::kChromeDevicePolicyType); |
| 198 | validator->ValidatePayload(); |
poromov | afc9d60e | 2017-02-28 16:38:10 | [diff] [blame] | 199 | if (force_immediate_load_) { |
| 200 | validator->RunValidation(); |
| 201 | ReportValidatorStatus(validator.get()); |
| 202 | } else { |
emaxx | 18f3a62 | 2017-04-19 13:37:09 | [diff] [blame] | 203 | policy::DeviceCloudPolicyValidator::StartValidation( |
| 204 | std::move(validator), |
Sergey Poromov | 0022e0f | 2019-11-20 16:50:02 | [diff] [blame] | 205 | base::BindOnce(&SessionManagerOperation::ReportValidatorStatus, |
| 206 | weak_factory_.GetWeakPtr())); |
poromov | afc9d60e | 2017-02-28 16:38:10 | [diff] [blame] | 207 | } |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | void SessionManagerOperation::ReportValidatorStatus( |
| 211 | policy::DeviceCloudPolicyValidator* validator) { |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 212 | if (validator->success()) { |
dcheng | 7c802f0 | 2015-12-31 16:09:55 | [diff] [blame] | 213 | policy_data_ = std::move(validator->policy_data()); |
| 214 | device_settings_ = std::move(validator->payload()); |
tnagel | efffaf0 | 2017-06-02 10:00:46 | [diff] [blame] | 215 | ReportResult(DeviceSettingsService::STORE_SUCCESS); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 216 | } else { |
Daniel Erat | a4285863 | 2018-09-28 18:20:51 | [diff] [blame] | 217 | LOG(ERROR) << "Policy validation failed: " << validator->status() << " (" |
| 218 | << policy::DeviceCloudPolicyValidator::StatusToString( |
| 219 | validator->status()) |
| 220 | << ")"; |
tnagel | efffaf0 | 2017-06-02 10:00:46 | [diff] [blame] | 221 | ReportResult(DeviceSettingsService::STORE_VALIDATION_ERROR); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 222 | } |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 223 | } |
| 224 | |
tnagel | 8d2791ec | 2016-11-18 10:43:13 | [diff] [blame] | 225 | LoadSettingsOperation::LoadSettingsOperation(bool force_key_load, |
| 226 | bool cloud_validations, |
poromov | afc9d60e | 2017-02-28 16:38:10 | [diff] [blame] | 227 | bool force_immediate_load, |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 228 | Callback callback) |
| 229 | : SessionManagerOperation(std::move(callback)) { |
tnagel | 8d2791ec | 2016-11-18 10:43:13 | [diff] [blame] | 230 | force_key_load_ = force_key_load; |
| 231 | cloud_validations_ = cloud_validations; |
poromov | afc9d60e | 2017-02-28 16:38:10 | [diff] [blame] | 232 | force_immediate_load_ = force_immediate_load; |
tnagel | 8d2791ec | 2016-11-18 10:43:13 | [diff] [blame] | 233 | } |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 234 | |
| 235 | LoadSettingsOperation::~LoadSettingsOperation() {} |
| 236 | |
| 237 | void LoadSettingsOperation::Run() { |
poromov | afc9d60e | 2017-02-28 16:38:10 | [diff] [blame] | 238 | if (force_immediate_load_) |
| 239 | LoadImmediately(); |
| 240 | else |
| 241 | StartLoading(); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 242 | } |
| 243 | |
[email protected] | ff9c5ecb | 2012-09-10 11:04:51 | [diff] [blame] | 244 | StoreSettingsOperation::StoreSettingsOperation( |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 245 | Callback callback, |
dcheng | 24002d0 | 2016-04-08 02:42:40 | [diff] [blame] | 246 | std::unique_ptr<em::PolicyFetchResponse> policy) |
Reilly Grant | 8f31d97617 | 2021-01-21 11:04:14 | [diff] [blame] | 247 | : SessionManagerOperation(std::move(callback)), policy_(std::move(policy)) { |
emaxx | 8aaab6c | 2017-03-07 14:57:33 | [diff] [blame] | 248 | if (policy_->has_new_public_key()) |
| 249 | force_key_load_ = true; |
| 250 | } |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 251 | |
| 252 | StoreSettingsOperation::~StoreSettingsOperation() {} |
| 253 | |
| 254 | void StoreSettingsOperation::Run() { |
| 255 | session_manager_client()->StoreDevicePolicy( |
[email protected] | ff9c5ecb | 2012-09-10 11:04:51 | [diff] [blame] | 256 | policy_->SerializeAsString(), |
Jan Wilken Dörrie | 5980169 | 2020-04-20 11:49:39 | [diff] [blame] | 257 | base::BindOnce(&StoreSettingsOperation::HandleStoreResult, |
| 258 | weak_factory_.GetWeakPtr())); |
[email protected] | 3798a56a | 2012-08-30 09:03:46 | [diff] [blame] | 259 | } |
| 260 | |
| 261 | void StoreSettingsOperation::HandleStoreResult(bool success) { |
| 262 | if (!success) |
| 263 | ReportResult(DeviceSettingsService::STORE_OPERATION_FAILED); |
| 264 | else |
| 265 | StartLoading(); |
| 266 | } |
| 267 | |
Yeunjoo Choi | d461f87 | 2021-03-11 06:52:19 | [diff] [blame^] | 268 | } // namespace ash |