Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / policy / remote_commands / device_command_screenshot_job.cc
blob93f24ebcc0cbf8ed7b9b802ab0fd4892f4e81949
1 // Copyright 2015 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.
5 #include "chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.h"
7 #include <fstream>
9 #include "ash/shell.h"
10 #include "base/bind.h"
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/threading/sequenced_worker_pool.h"
17 #include "base/values.h"
18 #include "chrome/browser/chromeos/policy/upload_job_impl.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "net/http/http_request_headers.h"
21 #include "policy/proto/device_management_backend.pb.h"
23 namespace policy {
25 namespace {
27 // Determines the time, measured from the time of issue, after which the command
28 // queue will consider this command expired if the command has not been started.
29 const int kCommandExpirationTimeInMinutes = 10;
31 // String constant identifying the result field in the result payload.
32 const char* const kResultFieldName = "result";
34 // Template string constant for populating the name field.
35 const char* const kNameFieldTemplate = "Screen %d";
37 // Template string constant for populating the name field.
38 const char* const kFilenameFieldTemplate = "screen%d.png";
40 // String constant identifying the header field which stores the command id.
41 const char* const kCommandIdHeaderName = "Command-ID";
43 // String constant signalling that the segment contains a png image.
44 const char* const kContentTypeImagePng = "image/png";
46 // String constant identifying the header field which stores the file type.
47 const char* const kFileTypeHeaderName = "File-Type";
49 // String constant signalling that the data segment contains screenshots.
50 const char* const kFileTypeScreenshotFile = "screenshot_file";
52 // String constant identifying the upload url field in the command payload.
53 const char* const kUploadUrlFieldName = "fileUploadUrl";
55 // A helper function which invokes |store_screenshot_callback| on |task_runner|.
56 void RunStoreScreenshotOnTaskRunner(
57 const ui::GrabWindowSnapshotAsyncPNGCallback& store_screenshot_callback,
58 scoped_refptr<base::TaskRunner> task_runner,
59 scoped_refptr<base::RefCountedBytes> png_data) {
60 task_runner->PostTask(FROM_HERE,
61 base::Bind(store_screenshot_callback, png_data));
64 } // namespace
66 class DeviceCommandScreenshotJob::Payload
67 : public RemoteCommandJob::ResultPayload {
68 public:
69 explicit Payload(ResultCode result_code);
70 ~Payload() override {}
72 // RemoteCommandJob::ResultPayload:
73 scoped_ptr<std::string> Serialize() override;
75 private:
76 std::string payload_;
78 DISALLOW_COPY_AND_ASSIGN(Payload);
81 DeviceCommandScreenshotJob::Payload::Payload(ResultCode result_code) {
82 base::DictionaryValue root_dict;
83 if (result_code != SUCCESS)
84 root_dict.SetInteger(kResultFieldName, result_code);
85 base::JSONWriter::Write(root_dict, &payload_);
88 scoped_ptr<std::string> DeviceCommandScreenshotJob::Payload::Serialize() {
89 return make_scoped_ptr(new std::string(payload_));
92 DeviceCommandScreenshotJob::DeviceCommandScreenshotJob(
93 scoped_ptr<Delegate> screenshot_delegate)
94 : num_pending_screenshots_(0),
95 screenshot_delegate_(screenshot_delegate.Pass()),
96 weak_ptr_factory_(this) {
97 DCHECK(screenshot_delegate_);
100 DeviceCommandScreenshotJob::~DeviceCommandScreenshotJob() {
103 enterprise_management::RemoteCommand_Type DeviceCommandScreenshotJob::GetType()
104 const {
105 return enterprise_management::RemoteCommand_Type_DEVICE_SCREENSHOT;
108 void DeviceCommandScreenshotJob::OnSuccess() {
109 base::ThreadTaskRunnerHandle::Get()->PostTask(
110 FROM_HERE,
111 base::Bind(succeeded_callback_,
112 base::Passed(make_scoped_ptr(new Payload(SUCCESS)))));
115 void DeviceCommandScreenshotJob::OnFailure(UploadJob::ErrorCode error_code) {
116 ResultCode result_code = FAILURE_CLIENT;
117 switch (error_code) {
118 case UploadJob::AUTHENTICATION_ERROR:
119 result_code = FAILURE_AUTHENTICATION;
120 break;
121 case UploadJob::NETWORK_ERROR:
122 case UploadJob::SERVER_ERROR:
123 result_code = FAILURE_SERVER;
124 break;
125 case UploadJob::CONTENT_ENCODING_ERROR:
126 result_code = FAILURE_CLIENT;
127 break;
129 base::ThreadTaskRunnerHandle::Get()->PostTask(
130 FROM_HERE,
131 base::Bind(failed_callback_,
132 base::Passed(make_scoped_ptr(new Payload(result_code)))));
135 bool DeviceCommandScreenshotJob::IsExpired(base::TimeTicks now) {
136 return now > issued_time() + base::TimeDelta::FromMinutes(
137 kCommandExpirationTimeInMinutes);
140 bool DeviceCommandScreenshotJob::ParseCommandPayload(
141 const std::string& command_payload) {
142 scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(command_payload));
143 if (!root.get())
144 return false;
145 base::DictionaryValue* payload = nullptr;
146 if (!root->GetAsDictionary(&payload))
147 return false;
148 std::string upload_url;
149 if (!payload->GetString(kUploadUrlFieldName, &upload_url))
150 return false;
151 upload_url_ = GURL(upload_url);
152 return true;
155 void DeviceCommandScreenshotJob::StoreScreenshot(
156 size_t screen,
157 scoped_refptr<base::RefCountedBytes> png_data) {
158 screenshots_.insert(std::make_pair(screen, png_data));
159 DCHECK_LT(0, num_pending_screenshots_);
160 --num_pending_screenshots_;
162 if (num_pending_screenshots_ == 0)
163 StartScreenshotUpload();
166 void DeviceCommandScreenshotJob::StartScreenshotUpload() {
167 for (const auto& screenshot_entry : screenshots_) {
168 std::map<std::string, std::string> header_fields;
169 header_fields.insert(
170 std::make_pair(kFileTypeHeaderName, kFileTypeScreenshotFile));
171 header_fields.insert(std::make_pair(net::HttpRequestHeaders::kContentType,
172 kContentTypeImagePng));
173 header_fields.insert(std::make_pair(kCommandIdHeaderName,
174 base::Uint64ToString(unique_id())));
175 scoped_ptr<std::string> data = make_scoped_ptr(
176 new std::string((const char*)screenshot_entry.second->front(),
177 screenshot_entry.second->size()));
178 upload_job_->AddDataSegment(
179 base::StringPrintf(kNameFieldTemplate, screenshot_entry.first),
180 base::StringPrintf(kFilenameFieldTemplate, screenshot_entry.first),
181 header_fields, data.Pass());
183 upload_job_->Start();
186 void DeviceCommandScreenshotJob::RunImpl(
187 const CallbackWithResult& succeeded_callback,
188 const CallbackWithResult& failed_callback) {
189 succeeded_callback_ = succeeded_callback;
190 failed_callback_ = failed_callback;
192 // Fail if the delegate says screenshots are not allowed in this session.
193 if (!screenshot_delegate_->IsScreenshotAllowed()) {
194 base::ThreadTaskRunnerHandle::Get()->PostTask(
195 FROM_HERE,
196 base::Bind(failed_callback_, base::Passed(make_scoped_ptr(
197 new Payload(FAILURE_USER_INPUT)))));
200 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
202 // Immediately fail if the upload url is invalid.
203 if (!upload_url_.is_valid()) {
204 LOG(ERROR) << upload_url_ << " is not a valid URL.";
205 base::ThreadTaskRunnerHandle::Get()->PostTask(
206 FROM_HERE,
207 base::Bind(failed_callback_, base::Passed(make_scoped_ptr(
208 new Payload(FAILURE_INVALID_URL)))));
209 return;
212 // Immediately fail if there are no attached screens.
213 if (root_windows.size() == 0) {
214 base::ThreadTaskRunnerHandle::Get()->PostTask(
215 FROM_HERE,
216 base::Bind(failed_callback_, base::Passed(make_scoped_ptr(new Payload(
217 FAILURE_SCREENSHOT_ACQUISITION)))));
218 return;
221 upload_job_ = screenshot_delegate_->CreateUploadJob(upload_url_, this);
222 DCHECK(upload_job_);
224 // Post tasks to the sequenced worker pool for taking screenshots on each
225 // attached screen.
226 num_pending_screenshots_ = root_windows.size();
227 for (size_t screen = 0; screen < root_windows.size(); ++screen) {
228 aura::Window* root_window = root_windows[screen];
229 gfx::Rect rect = root_window->bounds();
230 screenshot_delegate_->TakeSnapshot(
231 root_window, rect,
232 base::Bind(&RunStoreScreenshotOnTaskRunner,
233 base::Bind(&DeviceCommandScreenshotJob::StoreScreenshot,
234 weak_ptr_factory_.GetWeakPtr(), screen),
235 base::ThreadTaskRunnerHandle::Get()));
239 void DeviceCommandScreenshotJob::TerminateImpl() {
240 weak_ptr_factory_.InvalidateWeakPtrs();
243 } // namespace policy