Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(275)

Side by Side Diff: chrome/browser/policy/cloud/cloud_policy_invalidator.cc

Issue 23592017: Fix policy invalidator lifecycle bugs for Android. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/policy/cloud/cloud_policy_invalidator.h" 5 #include "chrome/browser/policy/cloud/cloud_policy_invalidator.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/command_line.h" 8 #include "base/command_line.h"
9 #include "base/location.h" 9 #include "base/location.h"
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
11 #include "base/rand_util.h" 11 #include "base/rand_util.h"
12 #include "base/sequenced_task_runner.h" 12 #include "base/sequenced_task_runner.h"
13 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_number_conversions.h"
14 #include "base/time/time.h" 14 #include "base/time/time.h"
15 #include "base/values.h" 15 #include "base/values.h"
16 #include "chrome/browser/invalidation/invalidation_service.h" 16 #include "chrome/browser/invalidation/invalidation_service.h"
17 #include "chrome/browser/invalidation/invalidation_service_factory.h" 17 #include "chrome/browser/policy/cloud/cloud_policy_client.h"
18 #include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h"
18 #include "chrome/browser/policy/cloud/enterprise_metrics.h" 19 #include "chrome/browser/policy/cloud/enterprise_metrics.h"
19 #include "chrome/common/chrome_switches.h" 20 #include "chrome/common/chrome_switches.h"
20 #include "policy/policy_constants.h" 21 #include "policy/policy_constants.h"
21 #include "sync/notifier/object_id_invalidation_map.h" 22 #include "sync/notifier/object_id_invalidation_map.h"
22 23
23 namespace policy { 24 namespace policy {
24 25
25 const int CloudPolicyInvalidator::kMissingPayloadDelay = 5; 26 const int CloudPolicyInvalidator::kMissingPayloadDelay = 5;
26 const int CloudPolicyInvalidator::kMaxFetchDelayDefault = 5000; 27 const int CloudPolicyInvalidator::kMaxFetchDelayDefault = 5000;
27 const int CloudPolicyInvalidator::kMaxFetchDelayMin = 1000; 28 const int CloudPolicyInvalidator::kMaxFetchDelayMin = 1000;
28 const int CloudPolicyInvalidator::kMaxFetchDelayMax = 300000; 29 const int CloudPolicyInvalidator::kMaxFetchDelayMax = 300000;
29 30
30 CloudPolicyInvalidator::CloudPolicyInvalidator( 31 CloudPolicyInvalidator::CloudPolicyInvalidator(
31 CloudPolicyInvalidationHandler* invalidation_handler, 32 CloudPolicyCore* core,
32 CloudPolicyStore* store,
33 const scoped_refptr<base::SequencedTaskRunner>& task_runner) 33 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
34 : invalidation_handler_(invalidation_handler), 34 : state_(UNINITIALIZED),
35 store_(store), 35 core_(core),
36 task_runner_(task_runner), 36 task_runner_(task_runner),
37 profile_(NULL),
38 invalidation_service_(NULL), 37 invalidation_service_(NULL),
39 invalidations_enabled_(false), 38 invalidations_enabled_(false),
40 invalidation_service_enabled_(false), 39 invalidation_service_enabled_(false),
41 registered_timestamp_(0), 40 registered_timestamp_(0),
42 invalid_(false), 41 invalid_(false),
43 invalidation_version_(0), 42 invalidation_version_(0),
44 unknown_version_invalidation_count_(0), 43 unknown_version_invalidation_count_(0),
45 ack_handle_(syncer::AckHandle::InvalidAckHandle()), 44 ack_handle_(syncer::AckHandle::InvalidAckHandle()),
46 weak_factory_(this), 45 weak_factory_(this),
47 max_fetch_delay_(kMaxFetchDelayDefault) { 46 max_fetch_delay_(kMaxFetchDelayDefault),
48 DCHECK(invalidation_handler); 47 policy_refresh_count_(0),
49 DCHECK(store); 48 invalidations_enabled_count_(0),
49 invalidations_disabled_count_(0) {
50 DCHECK(core);
50 DCHECK(task_runner.get()); 51 DCHECK(task_runner.get());
51 DCHECK(!IsInitialized());
52 } 52 }
53 53
54 CloudPolicyInvalidator::~CloudPolicyInvalidator() {} 54 CloudPolicyInvalidator::~CloudPolicyInvalidator() {
55 55 DCHECK(state_ == SHUT_DOWN);
56 void CloudPolicyInvalidator::InitializeWithProfile(Profile* profile) {
57 DCHECK(!IsInitialized());
58 DCHECK(profile);
59 profile_ = profile;
60 Initialize();
61 } 56 }
62 57
63 void CloudPolicyInvalidator::InitializeWithService( 58 void CloudPolicyInvalidator::Initialize(
64 invalidation::InvalidationService* invalidation_service) { 59 const GetInvalidationService& get_invalidation_service) {
65 DCHECK(!IsInitialized()); 60 DCHECK(state_ == UNINITIALIZED);
66 DCHECK(invalidation_service); 61 DCHECK(thread_checker_.CalledOnValidThread());
67 invalidation_service_ = invalidation_service; 62 DCHECK(!get_invalidation_service.is_null());
68 Initialize(); 63 get_invalidation_service_ = get_invalidation_service;
64 state_ = STOPPED;
65 core_->AddObserver(this);
66 if (core_->refresh_scheduler())
67 OnRefreshSchedulerStarted(core_);
69 } 68 }
70 69
71 void CloudPolicyInvalidator::Shutdown() { 70 void CloudPolicyInvalidator::Shutdown() {
72 if (IsInitialized()) { 71 DCHECK(state_ != SHUT_DOWN);
72 DCHECK(thread_checker_.CalledOnValidThread());
73 if (state_ == STARTED) {
73 if (registered_timestamp_) 74 if (registered_timestamp_)
74 invalidation_service_->UnregisterInvalidationHandler(this); 75 invalidation_service_->UnregisterInvalidationHandler(this);
75 store_->RemoveObserver(this); 76 core_->store()->RemoveObserver(this);
77 weak_factory_.InvalidateWeakPtrs();
76 } 78 }
79 if (state_ != UNINITIALIZED)
80 core_->RemoveObserver(this);
81 state_ = SHUT_DOWN;
77 } 82 }
78 83
79 void CloudPolicyInvalidator::OnInvalidatorStateChange( 84 void CloudPolicyInvalidator::OnInvalidatorStateChange(
80 syncer::InvalidatorState state) { 85 syncer::InvalidatorState state) {
86 DCHECK(state_ == STARTED);
81 DCHECK(thread_checker_.CalledOnValidThread()); 87 DCHECK(thread_checker_.CalledOnValidThread());
82 invalidation_service_enabled_ = state == syncer::INVALIDATIONS_ENABLED; 88 invalidation_service_enabled_ = state == syncer::INVALIDATIONS_ENABLED;
83 UpdateInvalidationsEnabled(); 89 UpdateInvalidationsEnabled();
84 } 90 }
85 91
86 void CloudPolicyInvalidator::OnIncomingInvalidation( 92 void CloudPolicyInvalidator::OnIncomingInvalidation(
87 const syncer::ObjectIdInvalidationMap& invalidation_map) { 93 const syncer::ObjectIdInvalidationMap& invalidation_map) {
94 DCHECK(state_ == STARTED);
88 DCHECK(thread_checker_.CalledOnValidThread()); 95 DCHECK(thread_checker_.CalledOnValidThread());
89 const syncer::ObjectIdInvalidationMap::const_iterator invalidation = 96 const syncer::ObjectIdInvalidationMap::const_iterator invalidation =
90 invalidation_map.find(object_id_); 97 invalidation_map.find(object_id_);
91 if (invalidation == invalidation_map.end()) { 98 if (invalidation == invalidation_map.end()) {
92 NOTREACHED(); 99 NOTREACHED();
93 return; 100 return;
94 } 101 }
95 HandleInvalidation(invalidation->second); 102 HandleInvalidation(invalidation->second);
96 } 103 }
97 104
105 void CloudPolicyInvalidator::OnCoreConnected(CloudPolicyCore* core) {}
106
107 void CloudPolicyInvalidator::OnRefreshSchedulerStarted(CloudPolicyCore* core) {
108 DCHECK(state_ == STOPPED);
109 DCHECK(thread_checker_.CalledOnValidThread());
110 state_ = STARTED;
111 OnStoreLoaded(core_->store());
112 core_->store()->AddObserver(this);
113 }
114
115 void CloudPolicyInvalidator::OnCoreDisconnected(CloudPolicyCore* core) {
116 DCHECK(state_ == STARTED || state_ == STOPPED);
117 DCHECK(thread_checker_.CalledOnValidThread());
118 if (state_ == STARTED) {
119 Unregister();
120 core_->store()->RemoveObserver(this);
121 state_ = STOPPED;
122 }
123 }
124
98 void CloudPolicyInvalidator::OnStoreLoaded(CloudPolicyStore* store) { 125 void CloudPolicyInvalidator::OnStoreLoaded(CloudPolicyStore* store) {
99 DCHECK(IsInitialized()); 126 DCHECK(state_ == STARTED);
100 DCHECK(thread_checker_.CalledOnValidThread()); 127 DCHECK(thread_checker_.CalledOnValidThread());
101 if (registered_timestamp_) { 128 if (registered_timestamp_) {
102 // Update the kMetricPolicyRefresh histogram. In some cases, this object can 129 // Update the kMetricPolicyRefresh histogram. In some cases, this object can
103 // be constructed during an OnStoreLoaded callback, which causes 130 // be constructed during an OnStoreLoaded callback, which causes
104 // OnStoreLoaded to be called twice at initialization time, so make sure 131 // OnStoreLoaded to be called twice at initialization time, so make sure
105 // that the timestamp does not match the timestamp at which registration 132 // that the timestamp does not match the timestamp at which registration
106 // occurred. We only measure changes which occur after registration. 133 // occurred. We only measure changes which occur after registration.
107 if (!store->policy() || !store->policy()->has_timestamp() || 134 if (!store->policy() || !store->policy()->has_timestamp() ||
108 store->policy()->timestamp() != registered_timestamp_) { 135 store->policy()->timestamp() != registered_timestamp_) {
109 UMA_HISTOGRAM_ENUMERATION( 136 UMA_HISTOGRAM_ENUMERATION(
110 kMetricPolicyRefresh, 137 kMetricPolicyRefresh,
111 GetPolicyRefreshMetric(), 138 GetPolicyRefreshMetric(),
112 METRIC_POLICY_REFRESH_SIZE); 139 METRIC_POLICY_REFRESH_SIZE);
113 } 140 }
114 141
115 // If the policy was invalid and the version stored matches the latest 142 // If the policy was invalid and the version stored matches the latest
116 // invalidation version, acknowledge the latest invalidation. 143 // invalidation version, acknowledge the latest invalidation.
117 if (invalid_ && store->invalidation_version() == invalidation_version_) 144 if (invalid_ && store->invalidation_version() == invalidation_version_)
118 AcknowledgeInvalidation(); 145 AcknowledgeInvalidation();
119 } 146 }
120 147
121 UpdateRegistration(store->policy()); 148 UpdateRegistration(store->policy());
122 UpdateMaxFetchDelay(store->policy_map()); 149 UpdateMaxFetchDelay(store->policy_map());
123 } 150 }
124 151
125 void CloudPolicyInvalidator::OnStoreError(CloudPolicyStore* store) {} 152 void CloudPolicyInvalidator::OnStoreError(CloudPolicyStore* store) {}
126 153
127 base::WeakPtr<CloudPolicyInvalidator> CloudPolicyInvalidator::GetWeakPtr() {
128 DCHECK(!IsInitialized());
129 return weak_factory_.GetWeakPtr();
130 }
131
132 void CloudPolicyInvalidator::Initialize() {
133 OnStoreLoaded(store_);
134 store_->AddObserver(this);
135 }
136
137 bool CloudPolicyInvalidator::IsInitialized() {
138 // Could have been initialized with a profile or invalidation service.
139 return profile_ || invalidation_service_;
140 }
141
142 void CloudPolicyInvalidator::HandleInvalidation( 154 void CloudPolicyInvalidator::HandleInvalidation(
143 const syncer::Invalidation& invalidation) { 155 const syncer::Invalidation& invalidation) {
144 // The invalidation service may send an invalidation more than once if there 156 // The invalidation service may send an invalidation more than once if there
145 // is a delay in acknowledging it. Duplicate invalidations are ignored. 157 // is a delay in acknowledging it. Duplicate invalidations are ignored.
146 if (invalid_ && ack_handle_.Equals(invalidation.ack_handle)) 158 if (invalid_ && ack_handle_.Equals(invalidation.ack_handle))
147 return; 159 return;
148 160
149 // If there is still a pending invalidation, acknowledge it, since we only 161 // If there is still a pending invalidation, acknowledge it, since we only
150 // care about the latest invalidation. 162 // care about the latest invalidation.
151 if (invalid_) 163 if (invalid_)
(...skipping 12 matching lines...) Expand all
164 invalidation_version_ = -(++unknown_version_invalidation_count_); 176 invalidation_version_ = -(++unknown_version_invalidation_count_);
165 177
166 // In order to prevent the cloud policy server from becoming overwhelmed when 178 // In order to prevent the cloud policy server from becoming overwhelmed when
167 // a policy with many users is modified, delay for a random period of time 179 // a policy with many users is modified, delay for a random period of time
168 // before fetching the policy. Delay for at least 20ms so that if multiple 180 // before fetching the policy. Delay for at least 20ms so that if multiple
169 // invalidations are received in quick succession, only one fetch will be 181 // invalidations are received in quick succession, only one fetch will be
170 // performed. 182 // performed.
171 base::TimeDelta delay = base::TimeDelta::FromMilliseconds( 183 base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
172 base::RandInt(20, max_fetch_delay_)); 184 base::RandInt(20, max_fetch_delay_));
173 185
174 // If there is a payload, the invalidate callback can run at any time, so set 186 // If there is a payload, the policy can be refreshed at any time, so set
175 // the version and payload on the client immediately. Otherwise, the callback 187 // the version and payload on the client immediately. Otherwise, the refresh
176 // must only run after at least kMissingPayloadDelay minutes. 188 // must only run after at least kMissingPayloadDelay minutes.
177 const std::string& payload = invalidation.payload; 189 const std::string& payload = invalidation.payload;
178 if (!invalidation.payload.empty()) 190 if (!invalidation.payload.empty())
179 invalidation_handler_->SetInvalidationInfo(invalidation_version_, payload); 191 SetInvalidationInfo(invalidation_version_, payload);
180 else 192 else
181 delay += base::TimeDelta::FromMinutes(kMissingPayloadDelay); 193 delay += base::TimeDelta::FromMinutes(kMissingPayloadDelay);
182 194
183 // Schedule the invalidate callback to run. 195 // Schedule the policy to be refreshed.
184 task_runner_->PostDelayedTask( 196 task_runner_->PostDelayedTask(
185 FROM_HERE, 197 FROM_HERE,
186 base::Bind( 198 base::Bind(
187 &CloudPolicyInvalidator::RunInvalidateCallback, 199 &CloudPolicyInvalidator::RefreshPolicy,
188 weak_factory_.GetWeakPtr(), 200 weak_factory_.GetWeakPtr(),
189 payload.empty() /* is_missing_payload */), 201 payload.empty() /* is_missing_payload */),
190 delay); 202 delay);
191 203
192 // Update the kMetricPolicyInvalidations histogram. 204 // Update the kMetricPolicyInvalidations histogram.
193 UMA_HISTOGRAM_BOOLEAN(kMetricPolicyInvalidations, !payload.empty()); 205 UMA_HISTOGRAM_BOOLEAN(kMetricPolicyInvalidations, !payload.empty());
194 } 206 }
195 207
196 void CloudPolicyInvalidator::UpdateRegistration( 208 void CloudPolicyInvalidator::UpdateRegistration(
197 const enterprise_management::PolicyData* policy) { 209 const enterprise_management::PolicyData* policy) {
(...skipping 12 matching lines...) Expand all
210 222
211 // If the policy object id in the policy data is different from the currently 223 // If the policy object id in the policy data is different from the currently
212 // registered object id, update the object registration. 224 // registered object id, update the object registration.
213 if (!registered_timestamp_ || !(object_id == object_id_)) 225 if (!registered_timestamp_ || !(object_id == object_id_))
214 Register(policy->timestamp(), object_id); 226 Register(policy->timestamp(), object_id);
215 } 227 }
216 228
217 void CloudPolicyInvalidator::Register( 229 void CloudPolicyInvalidator::Register(
218 int64 timestamp, 230 int64 timestamp,
219 const invalidation::ObjectId& object_id) { 231 const invalidation::ObjectId& object_id) {
220 // Get the invalidation service from the profile if needed. 232 // Get the invalidation service if needed.
221 if (!invalidation_service_) { 233 if (!invalidation_service_) {
222 DCHECK(profile_); 234 invalidation_service_ = get_invalidation_service_.Run();
223 invalidation_service_ =
224 invalidation::InvalidationServiceFactory::GetForProfile(profile_);
225 if (!invalidation_service_) 235 if (!invalidation_service_)
226 return; 236 return;
227 } 237 }
228 238
229 // Register this handler with the invalidation service if needed. 239 // Register this handler with the invalidation service if needed.
230 if (!registered_timestamp_) { 240 if (!registered_timestamp_) {
231 OnInvalidatorStateChange(invalidation_service_->GetInvalidatorState()); 241 OnInvalidatorStateChange(invalidation_service_->GetInvalidatorState());
232 invalidation_service_->RegisterInvalidationHandler(this); 242 invalidation_service_->RegisterInvalidationHandler(this);
233 } 243 }
234 244
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
288 max_fetch_delay_ = kMaxFetchDelayMax; 298 max_fetch_delay_ = kMaxFetchDelayMax;
289 else 299 else
290 max_fetch_delay_ = delay; 300 max_fetch_delay_ = delay;
291 } 301 }
292 302
293 void CloudPolicyInvalidator::UpdateInvalidationsEnabled() { 303 void CloudPolicyInvalidator::UpdateInvalidationsEnabled() {
294 bool invalidations_enabled = 304 bool invalidations_enabled =
295 invalidation_service_enabled_ && registered_timestamp_; 305 invalidation_service_enabled_ && registered_timestamp_;
296 if (invalidations_enabled_ != invalidations_enabled) { 306 if (invalidations_enabled_ != invalidations_enabled) {
297 invalidations_enabled_ = invalidations_enabled; 307 invalidations_enabled_ = invalidations_enabled;
298 invalidation_handler_->OnInvalidatorStateChanged(invalidations_enabled); 308 DCHECK(core_->refresh_scheduler());
Mattias Nissler (ping if slow) 2013/09/02 11:54:52 unneeded, we'll crash in the next line anyways
Steve Condie 2013/09/03 04:41:56 Done.
309 core_->refresh_scheduler()->SetInvalidationServiceAvailability(
310 invalidations_enabled);
311
312 // Update test counters
313 if (invalidations_enabled)
314 invalidations_enabled_count_++;
315 else
316 invalidations_disabled_count_++;
Mattias Nissler (ping if slow) 2013/09/02 11:54:52 Can't we just check invalidations_enabled on the r
Steve Condie 2013/09/03 04:41:56 Changed test to check state on refresh scheduler.
299 } 317 }
300 } 318 }
301 319
302 void CloudPolicyInvalidator::RunInvalidateCallback(bool is_missing_payload) { 320 void CloudPolicyInvalidator::SetInvalidationInfo(int64 invalidation_version,
321 const std::string& payload) {
322 DCHECK(core_->client());
Mattias Nissler (ping if slow) 2013/09/02 11:54:52 unneeded, we'll crash in the next line anyways
Steve Condie 2013/09/03 04:41:56 Done.
323 core_->client()->SetInvalidationInfo(invalidation_version, payload);
324 }
325
326
327 void CloudPolicyInvalidator::RefreshPolicy(bool is_missing_payload) {
303 DCHECK(thread_checker_.CalledOnValidThread()); 328 DCHECK(thread_checker_.CalledOnValidThread());
304 // In the missing payload case, the invalidation version has not been set on 329 // In the missing payload case, the invalidation version has not been set on
305 // the client yet, so set it now that the required time has elapsed. 330 // the client yet, so set it now that the required time has elapsed.
306 if (is_missing_payload) { 331 if (is_missing_payload)
307 invalidation_handler_->SetInvalidationInfo( 332 SetInvalidationInfo(invalidation_version_, std::string());
308 invalidation_version_, 333 DCHECK(core_->refresh_scheduler());
Mattias Nissler (ping if slow) 2013/09/02 11:54:52 unneeded, we'll crash in the next line anyways
Steve Condie 2013/09/03 04:41:56 Done.
309 std::string()); 334 core_->refresh_scheduler()->RefreshSoon();
310 } 335 policy_refresh_count_++;
Mattias Nissler (ping if slow) 2013/09/02 11:54:52 Can we just examine the state of the refresh sched
Steve Condie 2013/09/03 04:41:56 I don't think there's a very straightforward way t
Mattias Nissler (ping if slow) 2013/09/03 14:46:36 Can we just add an is_refresh_pending() function t
Steve Condie 2013/09/04 18:00:38 It's not quite that simple because a refresh is es
Mattias Nissler (ping if slow) 2013/09/05 14:54:04 Thought some more about this. Given that you're no
Steve Condie 2013/09/06 06:23:05 Tracked in bug 286213.
311 invalidation_handler_->InvalidatePolicy();
312 } 336 }
313 337
314 void CloudPolicyInvalidator::AcknowledgeInvalidation() { 338 void CloudPolicyInvalidator::AcknowledgeInvalidation() {
315 DCHECK(invalid_); 339 DCHECK(invalid_);
316 invalid_ = false; 340 invalid_ = false;
317 invalidation_handler_->SetInvalidationInfo(0, std::string()); 341 SetInvalidationInfo(0, std::string());
318 invalidation_service_->AcknowledgeInvalidation(object_id_, ack_handle_); 342 invalidation_service_->AcknowledgeInvalidation(object_id_, ack_handle_);
319 // Cancel any scheduled invalidate callbacks. 343 // Cancel any scheduled policy refreshes.
320 weak_factory_.InvalidateWeakPtrs(); 344 weak_factory_.InvalidateWeakPtrs();
321 } 345 }
322 346
323 int CloudPolicyInvalidator::GetPolicyRefreshMetric() { 347 int CloudPolicyInvalidator::GetPolicyRefreshMetric() {
324 if (store_->policy_changed()) { 348 if (core_->store()->policy_changed()) {
325 if (invalid_) 349 if (invalid_)
326 return METRIC_POLICY_REFRESH_INVALIDATED_CHANGED; 350 return METRIC_POLICY_REFRESH_INVALIDATED_CHANGED;
327 if (invalidations_enabled_) 351 if (invalidations_enabled_)
328 return METRIC_POLICY_REFRESH_CHANGED; 352 return METRIC_POLICY_REFRESH_CHANGED;
329 return METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS; 353 return METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS;
330 } 354 }
331 if (invalid_) 355 if (invalid_)
332 return METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED; 356 return METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED;
333 return METRIC_POLICY_REFRESH_UNCHANGED; 357 return METRIC_POLICY_REFRESH_UNCHANGED;
334 } 358 }
335 359
336 } // namespace policy 360 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698