OLD | NEW |
---|---|
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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/chromeos/printing/cups_print_job_manager_impl.h" | 5 #include "chrome/browser/chromeos/printing/cups_print_job_manager_impl.h" |
6 | 6 |
7 #include <cups/cups.h> | 7 #include <cups/cups.h> |
8 #include <set> | 8 #include <set> |
9 #include <string> | 9 #include <string> |
10 #include <utility> | 10 #include <utility> |
(...skipping 15 matching lines...) Expand all Loading... | |
26 #include "content/public/browser/notification_registrar.h" | 26 #include "content/public/browser/notification_registrar.h" |
27 #include "content/public/browser/notification_service.h" | 27 #include "content/public/browser/notification_service.h" |
28 #include "printing/backend/cups_connection.h" | 28 #include "printing/backend/cups_connection.h" |
29 #include "printing/printed_document.h" | 29 #include "printing/printed_document.h" |
30 | 30 |
31 namespace { | 31 namespace { |
32 | 32 |
33 // The rate in milliseconds at which we will poll CUPS for print job updates. | 33 // The rate in milliseconds at which we will poll CUPS for print job updates. |
34 const int kPollRate = 1000; | 34 const int kPollRate = 1000; |
35 | 35 |
36 // Threshold for giving up on communicating with CUPS. | |
37 const int kRetryMax = 6; | |
38 | |
36 // Returns the equivalient CupsPrintJob#State from a CupsJob#JobState. | 39 // Returns the equivalient CupsPrintJob#State from a CupsJob#JobState. |
37 chromeos::CupsPrintJob::State ConvertState(printing::CupsJob::JobState state) { | 40 chromeos::CupsPrintJob::State ConvertState(printing::CupsJob::JobState state) { |
38 using cpj = chromeos::CupsPrintJob::State; | 41 using cpj = chromeos::CupsPrintJob::State; |
39 | 42 |
40 switch (state) { | 43 switch (state) { |
41 case printing::CupsJob::PENDING: | 44 case printing::CupsJob::PENDING: |
42 return cpj::STATE_WAITING; | 45 return cpj::STATE_WAITING; |
43 case printing::CupsJob::HELD: | 46 case printing::CupsJob::HELD: |
44 return cpj::STATE_SUSPENDED; | 47 return cpj::STATE_SUSPENDED; |
45 case printing::CupsJob::PROCESSING: | 48 case printing::CupsJob::PROCESSING: |
(...skipping 16 matching lines...) Expand all Loading... | |
62 } | 65 } |
63 | 66 |
64 // Returns true if |state| represents a terminal state. | 67 // Returns true if |state| represents a terminal state. |
65 bool JobFinished(chromeos::CupsPrintJob::State state) { | 68 bool JobFinished(chromeos::CupsPrintJob::State state) { |
66 using chromeos::CupsPrintJob; | 69 using chromeos::CupsPrintJob; |
67 return state == CupsPrintJob::State::STATE_CANCELLED || | 70 return state == CupsPrintJob::State::STATE_CANCELLED || |
68 state == CupsPrintJob::State::STATE_ERROR || | 71 state == CupsPrintJob::State::STATE_ERROR || |
69 state == CupsPrintJob::State::STATE_DOCUMENT_DONE; | 72 state == CupsPrintJob::State::STATE_DOCUMENT_DONE; |
70 } | 73 } |
71 | 74 |
75 chromeos::QueryResult QueryCups(::printing::CupsConnection* connection, | |
76 const std::vector<std::string>& printer_ids) { | |
77 chromeos::QueryResult result; | |
78 result.success = connection->GetJobs(printer_ids, &(result.queues)); | |
79 return result; | |
80 } | |
81 | |
72 } // namespace | 82 } // namespace |
73 | 83 |
74 namespace chromeos { | 84 namespace chromeos { |
75 | 85 |
76 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile) | 86 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile) |
77 : CupsPrintJobManager(profile), | 87 : CupsPrintJobManager(profile), |
78 cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false), | 88 cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false), |
79 weak_ptr_factory_(this) { | 89 weak_ptr_factory_(this) { |
80 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, | 90 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, |
81 content::NotificationService::AllSources()); | 91 content::NotificationService::AllSources()); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
135 LOG(WARNING) << "Printer was removed while job was in progress. It cannot " | 145 LOG(WARNING) << "Printer was removed while job was in progress. It cannot " |
136 "be tracked"; | 146 "be tracked"; |
137 return false; | 147 return false; |
138 } | 148 } |
139 | 149 |
140 // Create a new print job. | 150 // Create a new print job. |
141 auto cpj = base::MakeUnique<CupsPrintJob>(*printer, job_id, title, | 151 auto cpj = base::MakeUnique<CupsPrintJob>(*printer, job_id, title, |
142 total_page_number); | 152 total_page_number); |
143 std::string key = cpj->GetUniqueId(); | 153 std::string key = cpj->GetUniqueId(); |
144 jobs_[key] = std::move(cpj); | 154 jobs_[key] = std::move(cpj); |
155 | |
145 CupsPrintJob* job = jobs_[key].get(); | 156 CupsPrintJob* job = jobs_[key].get(); |
146 NotifyJobCreated(job); | 157 NotifyJobCreated(job); |
147 | 158 |
148 // Always start jobs in the waiting state. | 159 // Always start jobs in the waiting state. |
149 job->set_state(CupsPrintJob::State::STATE_WAITING); | 160 job->set_state(CupsPrintJob::State::STATE_WAITING); |
150 NotifyJobUpdated(job); | 161 NotifyJobUpdated(job); |
151 | 162 |
152 ScheduleQuery(base::TimeDelta()); | 163 ScheduleQuery(base::TimeDelta()); |
153 | 164 |
154 return true; | 165 return true; |
155 } | 166 } |
156 | 167 |
157 void CupsPrintJobManagerImpl::ScheduleQuery() { | 168 void CupsPrintJobManagerImpl::ScheduleQuery() { |
158 ScheduleQuery(base::TimeDelta::FromMilliseconds(kPollRate)); | 169 ScheduleQuery(base::TimeDelta::FromMilliseconds(kPollRate)); |
159 } | 170 } |
160 | 171 |
161 void CupsPrintJobManagerImpl::ScheduleQuery(const base::TimeDelta& delay) { | 172 void CupsPrintJobManagerImpl::ScheduleQuery(const base::TimeDelta& delay) { |
162 if (!in_query_) { | 173 if (!in_query_) { |
163 in_query_ = true; | 174 in_query_ = true; |
164 content::BrowserThread::PostDelayedTask( | 175 |
165 content::BrowserThread::FILE_USER_BLOCKING, FROM_HERE, | 176 base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( |
166 base::Bind(&CupsPrintJobManagerImpl::QueryCups, | 177 FROM_HERE, |
178 base::Bind(&CupsPrintJobManagerImpl::PostQuery, | |
167 weak_ptr_factory_.GetWeakPtr()), | 179 weak_ptr_factory_.GetWeakPtr()), |
168 base::TimeDelta::FromMilliseconds(kPollRate)); | 180 delay); |
169 } | 181 } |
170 } | 182 } |
171 | 183 |
172 // Query CUPS asynchronously. Post results back to UI thread. | 184 // Query CUPS asynchronously. Post results back to UI thread. |
173 void CupsPrintJobManagerImpl::QueryCups() { | 185 void CupsPrintJobManagerImpl::PostQuery() { |
174 std::vector<::printing::CupsJob> jobs = cups_connection_.GetJobs(); | 186 // The set of active printers is expected to be small. |
187 std::set<std::string> printer_ids; | |
188 for (const auto& entry : jobs_) { | |
189 printer_ids.insert(entry.second->printer().id()); | |
190 } | |
191 std::vector<std::string> ids{printer_ids.begin(), printer_ids.end()}; | |
175 | 192 |
176 content::BrowserThread::PostTask( | 193 content::BrowserThread::PostTaskAndReplyWithResult( |
177 content::BrowserThread::ID::UI, FROM_HERE, | 194 content::BrowserThread::FILE_USER_BLOCKING, FROM_HERE, |
195 base::Bind(&QueryCups, &cups_connection_, ids), | |
178 base::Bind(&CupsPrintJobManagerImpl::UpdateJobs, | 196 base::Bind(&CupsPrintJobManagerImpl::UpdateJobs, |
179 weak_ptr_factory_.GetWeakPtr(), jobs)); | 197 weak_ptr_factory_.GetWeakPtr())); |
180 } | 198 } |
181 | 199 |
182 // Use job information to update local job states. Previously completed jobs | 200 // Use job information to update local job states. Previously completed jobs |
183 // could be in |jobs| but those are ignored as we will not emit updates for them | 201 // could be in |jobs| but those are ignored as we will not emit updates for them |
184 // after they are completed. | 202 // after they are completed. |
185 void CupsPrintJobManagerImpl::UpdateJobs( | 203 void CupsPrintJobManagerImpl::UpdateJobs(const QueryResult& result) { |
186 const std::vector<::printing::CupsJob>& jobs) { | 204 const std::vector<::printing::QueueStatus>& queues = result.queues; |
205 | |
206 // Query has completed. Allow more queries. | |
187 in_query_ = false; | 207 in_query_ = false; |
188 | 208 |
209 // If the query failed, either retry or purge. | |
210 if (!result.success) { | |
211 retry_count++; | |
212 LOG(WARNING) << "Failed to query CUPS for queue status. Schedule retry (" | |
213 << retry_count << ")"; | |
214 if (retry_count > kRetryMax) { | |
215 PurgeJobs(); | |
Carlson
2017/02/24 00:02:08
Does this deserve a "Giving up" log message?
skau
2017/02/28 00:59:58
Done.
| |
216 } else { | |
217 // Schedule another query with a larger delay. | |
218 DCHECK_GE(1, retry_count); | |
219 ScheduleQuery(base::TimeDelta::FromMilliseconds(kPollRate * retry_count)); | |
220 } | |
221 | |
Carlson
2017/02/24 00:02:08
Nit: Remove blank line
skau
2017/02/28 00:59:58
Done.
| |
222 return; | |
223 } | |
224 | |
225 // A query has completed. Reset retry counter. | |
226 retry_count = 0; | |
227 | |
189 std::vector<std::string> active_jobs; | 228 std::vector<std::string> active_jobs; |
190 for (auto& job : jobs) { | 229 for (const auto& queue : queues) { |
191 std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id); | 230 for (auto& job : queue.jobs) { |
192 const auto& entry = jobs_.find(key); | 231 std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id); |
193 if (entry != jobs_.end()) { | 232 const auto& entry = jobs_.find(key); |
233 if (entry == jobs_.end()) | |
234 continue; | |
235 | |
194 CupsPrintJob* print_job = entry->second.get(); | 236 CupsPrintJob* print_job = entry->second.get(); |
195 | 237 |
196 // Update a job we're tracking. | 238 // Update a job we're tracking. |
197 JobStateUpdated(print_job, ConvertState(job.state)); | 239 JobStateUpdated(print_job, ConvertState(job.state)); |
198 | 240 |
199 // Cleanup completed jobs. | 241 // Cleanup completed jobs. |
200 if (JobFinished(print_job->state())) { | 242 if (JobFinished(print_job->state())) { |
201 jobs_.erase(entry); | 243 jobs_.erase(entry); |
202 } else { | 244 } else { |
203 active_jobs.push_back(key); | 245 active_jobs.push_back(key); |
204 } | 246 } |
205 } | 247 } |
206 } | 248 } |
207 | 249 |
208 // Keep polling until all jobs complete or error. | 250 // Keep polling until all jobs complete or error. |
209 if (!active_jobs.empty()) { | 251 if (!active_jobs.empty()) { |
210 ScheduleQuery(); | 252 ScheduleQuery(); |
Carlson
2017/02/24 00:02:08
The fact that the reschedule here doesn't take int
skau
2017/02/28 00:59:58
Hmm. The interval isn't intended to be configurab
Carlson
2017/02/28 01:29:47
The custom poll rate option is just for testing?
Carlson
2017/02/28 18:32:02
Upon reading this more closely, I'm complaining ab
| |
211 } else if (!jobs_.empty()) { | 253 } else if (!jobs_.empty()) { |
212 // We're tracking jobs that we didn't receive an update for. Something bad | 254 // We're tracking jobs that we didn't receive an update for. Something bad |
213 // has happened. | 255 // has happened. |
214 LOG(ERROR) << "Lost track of (" << jobs_.size() << ") jobs"; | 256 LOG(ERROR) << "Lost track of (" << jobs_.size() << ") jobs"; |
215 for (const auto& entry : jobs_) { | 257 PurgeJobs(); |
216 // Declare all lost jobs errors. | |
217 JobStateUpdated(entry.second.get(), CupsPrintJob::State::STATE_ERROR); | |
218 } | |
219 | |
220 jobs_.clear(); | |
221 } | 258 } |
222 } | 259 } |
223 | 260 |
261 void CupsPrintJobManagerImpl::PurgeJobs() { | |
262 for (const auto& entry : jobs_) { | |
263 // Declare all lost jobs errors. | |
264 JobStateUpdated(entry.second.get(), CupsPrintJob::State::STATE_ERROR); | |
265 } | |
266 | |
267 jobs_.clear(); | |
268 } | |
269 | |
224 void CupsPrintJobManagerImpl::JobStateUpdated(CupsPrintJob* job, | 270 void CupsPrintJobManagerImpl::JobStateUpdated(CupsPrintJob* job, |
225 CupsPrintJob::State new_state) { | 271 CupsPrintJob::State new_state) { |
226 if (job->state() == new_state) | 272 if (job->state() == new_state) |
227 return; | 273 return; |
228 | 274 |
229 // We don't track state transitions because some of them might be missed due | 275 // We don't track state transitions because some of them might be missed due |
230 // to how we query jobs. | 276 // to how we query jobs. |
231 job->set_state(new_state); | 277 job->set_state(new_state); |
232 switch (new_state) { | 278 switch (new_state) { |
233 case CupsPrintJob::State::STATE_NONE: | 279 case CupsPrintJob::State::STATE_NONE: |
(...skipping 25 matching lines...) Expand all Loading... | |
259 break; | 305 break; |
260 } | 306 } |
261 } | 307 } |
262 | 308 |
263 // static | 309 // static |
264 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) { | 310 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) { |
265 return new CupsPrintJobManagerImpl(profile); | 311 return new CupsPrintJobManagerImpl(profile); |
266 } | 312 } |
267 | 313 |
268 } // namespace chromeos | 314 } // namespace chromeos |
OLD | NEW |