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: |
46 return cpj::STATE_STARTED; | 49 return cpj::STATE_STARTED; |
47 case printing::CupsJob::CANCELED: | 50 case printing::CupsJob::CANCELED: |
48 return cpj::STATE_CANCELLED; | 51 return cpj::STATE_CANCELLED; |
49 case printing::CupsJob::COMPLETED: | 52 case printing::CupsJob::COMPLETED: |
50 return cpj::STATE_DOCUMENT_DONE; | 53 return cpj::STATE_DOCUMENT_DONE; |
51 case printing::CupsJob::STOPPED: | 54 case printing::CupsJob::STOPPED: |
52 return cpj::STATE_SUSPENDED; | 55 return cpj::STATE_SUSPENDED; |
53 case printing::CupsJob::ABORTED: | 56 case printing::CupsJob::ABORTED: |
54 return cpj::STATE_ERROR; | 57 return cpj::STATE_ERROR; |
55 case printing::CupsJob::UNKNOWN: | 58 case printing::CupsJob::UNKNOWN: |
56 break; | 59 break; |
57 } | 60 } |
58 | 61 |
59 NOTREACHED(); | 62 NOTREACHED(); |
60 | 63 |
61 return cpj::STATE_NONE; | 64 return cpj::STATE_NONE; |
62 } | 65 } |
63 | 66 |
67 chromeos::QueryResult QueryCups(::printing::CupsConnection* connection, | |
68 const std::vector<std::string>& printer_ids) { | |
69 chromeos::QueryResult result; | |
70 result.success = connection->GetJobs(printer_ids, &(result.queues)); | |
Lei Zhang
2017/03/07 23:31:29
nit: Do you need the extra parenthesis around resu
skau
2017/03/10 01:07:00
Done.
| |
71 return result; | |
72 } | |
73 | |
64 } // namespace | 74 } // namespace |
65 | 75 |
66 namespace chromeos { | 76 namespace chromeos { |
67 | 77 |
68 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile) | 78 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile) |
69 : CupsPrintJobManager(profile), | 79 : CupsPrintJobManager(profile), |
70 cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false), | 80 cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false), |
71 weak_ptr_factory_(this) { | 81 weak_ptr_factory_(this) { |
72 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, | 82 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, |
73 content::NotificationService::AllSources()); | 83 content::NotificationService::AllSources()); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
127 LOG(WARNING) << "Printer was removed while job was in progress. It cannot " | 137 LOG(WARNING) << "Printer was removed while job was in progress. It cannot " |
128 "be tracked"; | 138 "be tracked"; |
129 return false; | 139 return false; |
130 } | 140 } |
131 | 141 |
132 // Create a new print job. | 142 // Create a new print job. |
133 auto cpj = base::MakeUnique<CupsPrintJob>(*printer, job_id, title, | 143 auto cpj = base::MakeUnique<CupsPrintJob>(*printer, job_id, title, |
134 total_page_number); | 144 total_page_number); |
135 std::string key = cpj->GetUniqueId(); | 145 std::string key = cpj->GetUniqueId(); |
136 jobs_[key] = std::move(cpj); | 146 jobs_[key] = std::move(cpj); |
147 | |
137 CupsPrintJob* job = jobs_[key].get(); | 148 CupsPrintJob* job = jobs_[key].get(); |
138 NotifyJobCreated(job); | 149 NotifyJobCreated(job); |
139 | 150 |
140 // Always start jobs in the waiting state. | 151 // Always start jobs in the waiting state. |
141 job->set_state(CupsPrintJob::State::STATE_WAITING); | 152 job->set_state(CupsPrintJob::State::STATE_WAITING); |
142 NotifyJobUpdated(job); | 153 NotifyJobUpdated(job); |
143 | 154 |
144 ScheduleQuery(base::TimeDelta()); | 155 ScheduleQuery(base::TimeDelta()); |
145 | 156 |
146 return true; | 157 return true; |
147 } | 158 } |
148 | 159 |
149 void CupsPrintJobManagerImpl::ScheduleQuery() { | 160 void CupsPrintJobManagerImpl::ScheduleQuery() { |
150 ScheduleQuery(base::TimeDelta::FromMilliseconds(kPollRate)); | 161 ScheduleQuery(base::TimeDelta::FromMilliseconds(kPollRate)); |
151 } | 162 } |
152 | 163 |
153 void CupsPrintJobManagerImpl::ScheduleQuery(const base::TimeDelta& delay) { | 164 void CupsPrintJobManagerImpl::ScheduleQuery(const base::TimeDelta& delay) { |
154 if (!in_query_) { | 165 if (!in_query_) { |
155 in_query_ = true; | 166 in_query_ = true; |
156 content::BrowserThread::PostDelayedTask( | 167 |
157 content::BrowserThread::FILE_USER_BLOCKING, FROM_HERE, | 168 base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( |
158 base::Bind(&CupsPrintJobManagerImpl::QueryCups, | 169 FROM_HERE, |
170 base::Bind(&CupsPrintJobManagerImpl::PostQuery, | |
159 weak_ptr_factory_.GetWeakPtr()), | 171 weak_ptr_factory_.GetWeakPtr()), |
160 base::TimeDelta::FromMilliseconds(kPollRate)); | 172 delay); |
161 } | 173 } |
162 } | 174 } |
163 | 175 |
164 // Query CUPS asynchronously. Post results back to UI thread. | 176 // Query CUPS asynchronously. Post results back to UI thread. |
Lei Zhang
2017/03/07 23:31:29
Can you move this to the header and consolidate it
skau
2017/03/10 01:07:00
Done.
| |
165 void CupsPrintJobManagerImpl::QueryCups() { | 177 void CupsPrintJobManagerImpl::PostQuery() { |
166 std::vector<::printing::CupsJob> jobs = cups_connection_.GetJobs(); | 178 // The set of active printers is expected to be small. |
179 std::set<std::string> printer_ids; | |
180 for (const auto& entry : jobs_) { | |
181 printer_ids.insert(entry.second->printer().id()); | |
182 } | |
183 std::vector<std::string> ids{printer_ids.begin(), printer_ids.end()}; | |
167 | 184 |
168 content::BrowserThread::PostTask( | 185 content::BrowserThread::PostTaskAndReplyWithResult( |
169 content::BrowserThread::ID::UI, FROM_HERE, | 186 content::BrowserThread::FILE_USER_BLOCKING, FROM_HERE, |
187 base::Bind(&QueryCups, &cups_connection_, ids), | |
170 base::Bind(&CupsPrintJobManagerImpl::UpdateJobs, | 188 base::Bind(&CupsPrintJobManagerImpl::UpdateJobs, |
171 weak_ptr_factory_.GetWeakPtr(), jobs)); | 189 weak_ptr_factory_.GetWeakPtr())); |
172 } | 190 } |
173 | 191 |
174 // Use job information to update local job states. Previously completed jobs | 192 // Use job information to update local job states. Previously completed jobs |
175 // could be in |jobs| but those are ignored as we will not emit updates for them | 193 // could be in |jobs| but those are ignored as we will not emit updates for them |
176 // after they are completed. | 194 // after they are completed. |
177 void CupsPrintJobManagerImpl::UpdateJobs( | 195 void CupsPrintJobManagerImpl::UpdateJobs(const QueryResult& result) { |
178 const std::vector<::printing::CupsJob>& jobs) { | 196 const std::vector<::printing::QueueStatus>& queues = result.queues; |
197 | |
198 // Query has completed. Allow more queries. | |
179 in_query_ = false; | 199 in_query_ = false; |
180 | 200 |
201 // If the query failed, either retry or purge. | |
202 if (!result.success) { | |
203 retry_count_++; | |
204 LOG(WARNING) << "Failed to query CUPS for queue status. Schedule retry (" | |
205 << retry_count_ << ")"; | |
206 if (retry_count_ > kRetryMax) { | |
207 LOG(ERROR) << "CUPS is unreachable. Giving up on all jobs."; | |
208 PurgeJobs(); | |
209 } else { | |
210 // Schedule another query with a larger delay. | |
211 DCHECK_GE(1, retry_count_); | |
212 ScheduleQuery( | |
213 base::TimeDelta::FromMilliseconds(kPollRate * retry_count_)); | |
214 } | |
215 return; | |
216 } | |
217 | |
218 // A query has completed. Reset retry counter. | |
219 retry_count_ = 0; | |
220 | |
181 std::vector<std::string> active_jobs; | 221 std::vector<std::string> active_jobs; |
182 for (auto& job : jobs) { | 222 for (const auto& queue : queues) { |
183 std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id); | 223 for (auto& job : queue.jobs) { |
184 const auto& entry = jobs_.find(key); | 224 std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id); |
185 if (entry != jobs_.end()) { | 225 const auto& entry = jobs_.find(key); |
226 if (entry == jobs_.end()) | |
227 continue; | |
228 | |
186 CupsPrintJob* print_job = entry->second.get(); | 229 CupsPrintJob* print_job = entry->second.get(); |
187 | 230 |
188 // Update a job we're tracking. | 231 // Update a job we're tracking. |
189 JobStateUpdated(print_job, ConvertState(job.state)); | 232 JobStateUpdated(print_job, ConvertState(job.state)); |
190 | 233 |
191 // Cleanup completed jobs. | 234 // Cleanup completed jobs. |
192 if (print_job->IsJobFinished()) { | 235 if (print_job->IsJobFinished()) { |
193 jobs_.erase(entry); | 236 jobs_.erase(entry); |
194 } else { | 237 } else { |
195 active_jobs.push_back(key); | 238 active_jobs.push_back(key); |
196 } | 239 } |
197 } | 240 } |
198 } | 241 } |
199 | 242 |
200 // Keep polling until all jobs complete or error. | 243 // Keep polling until all jobs complete or error. |
201 if (!active_jobs.empty()) { | 244 if (!active_jobs.empty()) { |
245 // During normal operations, we poll at the default rate. | |
202 ScheduleQuery(); | 246 ScheduleQuery(); |
203 } else if (!jobs_.empty()) { | 247 } else if (!jobs_.empty()) { |
204 // We're tracking jobs that we didn't receive an update for. Something bad | 248 // We're tracking jobs that we didn't receive an update for. Something bad |
205 // has happened. | 249 // has happened. |
206 LOG(ERROR) << "Lost track of (" << jobs_.size() << ") jobs"; | 250 LOG(ERROR) << "Lost track of (" << jobs_.size() << ") jobs"; |
207 for (const auto& entry : jobs_) { | 251 PurgeJobs(); |
208 // Declare all lost jobs errors. | |
209 JobStateUpdated(entry.second.get(), CupsPrintJob::State::STATE_ERROR); | |
210 } | |
211 | |
212 jobs_.clear(); | |
213 } | 252 } |
214 } | 253 } |
215 | 254 |
255 void CupsPrintJobManagerImpl::PurgeJobs() { | |
256 for (const auto& entry : jobs_) { | |
257 // Declare all lost jobs errors. | |
258 JobStateUpdated(entry.second.get(), CupsPrintJob::State::STATE_ERROR); | |
259 } | |
260 | |
261 jobs_.clear(); | |
262 } | |
263 | |
216 void CupsPrintJobManagerImpl::JobStateUpdated(CupsPrintJob* job, | 264 void CupsPrintJobManagerImpl::JobStateUpdated(CupsPrintJob* job, |
217 CupsPrintJob::State new_state) { | 265 CupsPrintJob::State new_state) { |
218 if (job->state() == new_state) | 266 if (job->state() == new_state) |
219 return; | 267 return; |
220 | 268 |
221 // We don't track state transitions because some of them might be missed due | 269 // We don't track state transitions because some of them might be missed due |
222 // to how we query jobs. | 270 // to how we query jobs. |
223 job->set_state(new_state); | 271 job->set_state(new_state); |
224 switch (new_state) { | 272 switch (new_state) { |
225 case CupsPrintJob::State::STATE_NONE: | 273 case CupsPrintJob::State::STATE_NONE: |
(...skipping 25 matching lines...) Expand all Loading... | |
251 break; | 299 break; |
252 } | 300 } |
253 } | 301 } |
254 | 302 |
255 // static | 303 // static |
256 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) { | 304 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) { |
257 return new CupsPrintJobManagerImpl(profile); | 305 return new CupsPrintJobManagerImpl(profile); |
258 } | 306 } |
259 | 307 |
260 } // namespace chromeos | 308 } // namespace chromeos |
OLD | NEW |